Skip to content

Feature/monorepo #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed

Conversation

RodrigooDS
Copy link

@RodrigooDS RodrigooDS commented Mar 30, 2025

Summary by CodeRabbit

  • New Features
    • Introduced robust API endpoints for user authentication and quote management, enabling secure registration, login, and real-time currency conversion.
  • Documentation
    • Updated project guides to reflect the new Nx workspace structure and improved developer instructions.
  • Tests
    • Expanded testing coverage across authentication, quota creation, and user management to ensure reliable functionality.
  • Chores
    • Streamlined project configuration by updating build settings, dependency versions, and development tools while removing outdated assets.

Copy link

coderabbitai bot commented Mar 30, 2025

Walkthrough

This pull request restructures the project into an Nx monorepo with a comprehensive reorganization of configuration, documentation, and testing files. Legacy NestJS files and older ESLint configurations have been removed, and new ESLint, Jest, and TypeScript configurations have been introduced. New modules, controllers, use cases, domain models, and services were added for handling authentication and currency quota operations. Additionally, Docker, Prisma schema, and updated package metadata now support PostgreSQL and modern dependency management. Overall, the changes refocus the project structure and build system for a modular, scalable application.

Changes

File(s) Change Summary
.eslintrc.js
**/eslint.config.mjs (in apps & libs)
Removed legacy ESLint file; introduced new ESLint config files using base presets in multiple directories (crypto-quote-monorepo, core, infrastructure, nest, shared).
.gitignore
.prettierignore
.prettierrc
Reorganized ignore patterns and formatting rules; added new prettier ignore file and enhanced Prettier configuration.
.vscode/settings.json New VSCode settings file added with format-on-save, import organization, and custom spell-check dictionary.
README.md (root & in libs)
SPEC.md
Updated root README to focus on Nx workspace setup and added SPEC.md detailing a NestJS-based currency conversion API challenge; new README files were introduced in several libraries.
jest.config.ts
jest.preset.js
New Jest configuration files added/updated for various applications and libraries to streamline testing setups.
Multiple tsconfig*.json files
tsconfig.base.json
New and updated TS configuration files for apps and libs ensuring strict type checking, proper outDirs, and path mappings across the monorepo.
apps/crypto-quote-monorepo/** (controllers, modules, main.ts, webpack.config.js, project.json) Introduced a new NestJS-based API featuring QuotaController, AuthController, QuotaModule, AuthModule, main bootstrap file, and associated build configurations with Webpack and project.json.
libs/core/domain*
libs/core/domain-services*
libs/core/use-cases*
Added new domain models (e.g. Quota, Cryptomkt, AuthResponse, User), abstract service/repository classes, use case implementations (CreateQuotaUseCase, CreateUserUseCase, GetQuotaUseCase, GetUserUseCase), and corresponding mocks and tests.
libs/infrastructure/** Added Prisma schema, Docker Compose config for PostgreSQL, new repository implementations (PrismaQuotaRepository, PrismaUserRepository), and service implementations (CryptomktServiceImpl, AuthServiceImpl, QuotaServiceImpl, UserServiceImpl) with tests.
nx.json
package.json
Updated package metadata, dependencies, and introduced an Nx workspace configuration with defined inputs and plugin settings.
Deleted Files:
src/app.controller*.ts
src/app.module.ts
src/app.service.ts
src/main.ts
test/app.e2e-spec.ts
test/jest-e2e.json
tsconfig.json (root)
tsconfig.build.json
Removed legacy NestJS application files, outdated test configs, and old TS configuration files in favor of the new Nx-structured setup.
apps/crypto-quote-monorepo/src/api/auth/** New authentication flow introduced with AuthController, DTO validations (CreateUserDto and LoginUserDto tests), and a JWT auth guard handling token extraction and verification.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant AC as AuthController
    participant CU as CreateUserUseCase / GetUserUseCase
    participant AS as AuthServiceImpl
    participant JWT as JwtService

    Client->>AC: POST /auth/register (or /login) with credentials
    AC->>CU: execute(user data)
    CU->>AS: Call register/login(email, password)
    AS->>AS: Validate credentials and hash passwords if needed
    AS->>JWT: Generate JWT token (using secret & expiration)
    JWT-->>AS: Return token
    AS-->>CU: Return AuthResponse with token, expires_in
    CU-->>AC: Return AuthResponse
    AC-->>Client: Respond with AuthResponse
Loading
sequenceDiagram
    participant Client as Client
    participant QC as QuotaController
    participant CQU as CreateQuotaUseCase
    participant CMS as CryptomktServiceImpl
    participant QS as QuotaServiceImpl
    participant DB as Database/Prisma

    Client->>QC: POST /quota with CreateQuoteDto
    QC->>CQU: execute(dto)
    CQU->>CQU: Validate quota input
    CQU->>CMS: getExchangeRate(from, to)
    CMS-->>CQU: Return exchange rate
    CQU->>CQU: Calculate converted amount
    CQU->>QS: create(quota)
    QS->>DB: Persist quota record
    DB-->>QS: Acknowledge creation
    QS-->>CQU: Return full quota object
    CQU-->>QC: Return quota object
    QC-->>Client: Respond with quota data
Loading

Poem

I'm a bunny with a twitchy nose,
Hoping over code where logic grows.
New modules bound like fields of clover,
ESLint and tests - I cheer, "Discover!"
In a monorepo, my burrow’s refined,
With JWTs and quotas so divinely aligned.
Hoppin’ through changes, I'm simply overjoyed!


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (39)
.gitignore (2)

1-7: Compiled Output Section – Consistency Check
The new entries for compiled outputs (dist, tmp, and /out-tsc) are clearly organized. Consider whether the mix of relative paths (e.g., dist, tmp) and an absolute path (/out-tsc) is intentional. Consistent path styles can help avoid potential confusion in larger teams.


35-39: Prisma Section – Migrations and Environment Files
Including prisma/migrations/ and generated/ helps keep auto-generated artifacts out of version control. However, adding .env here may require extra caution as environment files often contain sensitive data. Ensure that this aligns with your deployment strategy (e.g., using .env.example for defaults).

libs/infrastructure/project.json (1)

7-8: JSON Comment and Targets Field

The inline comment in line 7 provides useful guidance on viewing targets; however, note that standard JSON does not support comments. Verify that the Nx tooling accepts JSON with comments in these configuration files.

libs/core/domain/src/lib/models/quota.models.ts (1)

1-10: Consider enhancing the Quota model with constructor and validation.

The current implementation defines properties without initialization or validation, which could lead to incomplete or inconsistent objects.

Consider implementing:

  1. A constructor with required parameters
  2. Property validation
  3. Access modifiers for better encapsulation
 export class Quota {
-  id: string;
-  from: string;
-  to: string;
-  amount: number;
-  rate: number;
-  convertedAmount: number;
-  timestamp: Date;
-  expiresAt: Date;
+  constructor(
+    public readonly id: string,
+    public readonly from: string,
+    public readonly to: string,
+    public readonly amount: number,
+    public readonly rate: number,
+    public readonly convertedAmount: number,
+    public readonly timestamp: Date = new Date(),
+    public readonly expiresAt: Date
+  ) {
+    if (amount <= 0) throw new Error('Amount must be positive');
+    if (rate <= 0) throw new Error('Rate must be positive');
+    if (expiresAt < timestamp) throw new Error('Expiration date must be in the future');
+  }
 }
libs/core/domain/src/lib/models/cryptomkt.models.ts (1)

1-5: Add documentation and consider model improvements.

The Cryptomkt class provides a clear structure but lacks documentation. Consider these improvements:

  1. Add JSDoc comments to explain the purpose of the class and its properties
  2. Consider adding a constructor with optional parameter defaults
  3. Note that using string for price (rather than number) might be intentional to avoid floating-point precision issues, but it could be worth documenting this choice
+/**
+ * Represents cryptocurrency market data from CryptoMKT exchange
+ */
 export class Cryptomkt {
+  /**
+   * The currency code (e.g., 'BTC', 'ETH')
+   */
   currency: string;
+  /**
+   * The price as a string to preserve precision
+   */
   price: string;
+  /**
+   * The timestamp when this price was recorded
+   */
   timestamp: Date;
+  
+  constructor(data?: Partial<Cryptomkt>) {
+    Object.assign(this, data);
+  }
 }
libs/shared/project.json (1)

1-9: Consider adding basic targets to the shared library configuration.

The configuration is well-structured but includes an empty targets object. While the comment on line 7 provides guidance, consider adding basic targets like 'lint' and 'test' to ensure the library can be properly built, tested, and linted with Nx commands.

-  "targets": {}
+  "targets": {
+    "lint": {
+      "executor": "@nrwl/linter:eslint",
+      "options": {
+        "lintFilePatterns": ["libs/shared/**/*.ts"]
+      }
+    },
+    "test": {
+      "executor": "@nrwl/jest:jest",
+      "outputs": ["coverage/libs/shared"],
+      "options": {
+        "jestConfig": "libs/shared/jest.config.ts"
+      }
+    }
+  }
libs/infrastructure/src/lib/models/responses/cryptomkt.models.ts (1)

4-13: Add validation for response properties

The CryptomktResponse class correctly implements the Cryptomkt interface with proper @expose() decorators for serialization. Consider adding validation for better data integrity.

 import { Cryptomkt } from '@monorepo/core-domain';
 import { Expose } from 'class-transformer';
+import { IsString, IsDate, IsNotEmpty } from 'class-validator';

 export class CryptomktResponse implements Cryptomkt {
   @Expose()
+  @IsString()
+  @IsNotEmpty()
   currency: string;

   @Expose()
+  @IsString()
+  @IsNotEmpty()
   price: string;

   @Expose()
+  @IsDate()
   timestamp: Date;
 }
libs/infrastructure/src/lib/models/responses/quota.models.ts (1)

1-27: Consider adding validation to response model

The GetQuotaResponse class is well-structured with appropriate @Expose() decorators for serialization. Consider enhancing it with validation decorators from 'class-validator' to ensure data integrity, especially for numeric and date properties.

Example implementation:

 import { Expose } from 'class-transformer';
+import { IsDate, IsNumber, IsString } from 'class-validator';

 export class GetQuotaResponse {
   @Expose()
+  @IsString()
   id: string;

   @Expose()
+  @IsString()
   from: string;

   @Expose()
+  @IsString()
   to: string;

   @Expose()
+  @IsNumber()
   amount: number;

   @Expose()
+  @IsNumber()
   rate: number;

   @Expose()
+  @IsNumber()
   convertedAmount: number;

   @Expose()
+  @IsDate()
   timestamp: Date;

   @Expose()
+  @IsDate()
   expiresAt: Date;
 }
apps/crypto-quote-monorepo/tsconfig.json (1)

1-17: Review: tsconfig for Crypto Quote Monorepo App
The TypeScript configuration is minimal and clearly extends from the base configuration while referencing both tsconfig.app.json and tsconfig.spec.json. One point to note is the explicit declaration of empty "files": [] and "include": [] arrays. Please verify that these empty arrays are intentional since they prevent automatic inclusion of files—commonly, the referenced configuration files handle file inclusion.

apps/crypto-quote-monorepo/src/api/quota/dtos/quota.dto.ts (1)

3-16: Consider adding documentation and addressing precision concerns

The DTO structure and validation are good, but there are two improvements to consider:

  1. Add JSDoc comments to describe the purpose of the class and its properties
  2. For financial amounts, consider using a more precise type than native JavaScript number to avoid floating-point precision issues
+/**
+ * Data Transfer Object for creating a quote request
+ */
 export class CreateQuoteDto {
+  /**
+   * The amount to be converted - must be a positive number
+   */
   @IsNotEmpty()
   @IsNumber()
   @Min(0)
   amount: number;

+  /**
+   * The source currency code
+   */
   @IsNotEmpty()
   @IsString()
   from: string;

+  /**
+   * The target currency code
+   */
   @IsNotEmpty()
   @IsString()
   to: string;
 }

For handling financial amounts in production applications, consider using a decimal library like decimal.js or big.js:

// Example with decimal.js
import { IsNotEmpty, IsString } from 'class-validator';
import { Transform } from 'class-transformer';
import Decimal from 'decimal.js';

export class CreateQuoteDto {
  @IsNotEmpty()
  @Transform(({ value }) => new Decimal(value))
  amount: Decimal;
  
  // rest of the DTO
}
libs/nest/src/lib/filters/http-exception.filter.ts (2)

13-21: Consider adding more specific error type handling

The current implementation handles strings and generic objects, but you might want to add more specific handling for common error types or validation errors.

 let message = 'An unexpected error occurred';
 let errorDetails: any = null;

 if (typeof exceptionResponse === 'string') {
   message = exceptionResponse;
 } else if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {
   message = (exceptionResponse as any).message || message;
   errorDetails = (exceptionResponse as any).error || null;
+  // Handle validation errors specifically
+  if (Array.isArray((exceptionResponse as any).messages)) {
+    errorDetails = (exceptionResponse as any).messages;
+  }
 }

14-14: Type 'any' should be avoided where possible

Using 'any' reduces type safety. Consider creating a proper interface for error details.

-let errorDetails: any = null;
+interface ErrorDetails {
+  [key: string]: unknown;
+}
+let errorDetails: ErrorDetails | null = null;
apps/crypto-quote-monorepo/tsconfig.app.json (2)

9-9: Consider using a more recent ECMAScript target

ES2021 is now a bit dated. Consider updating to ES2022 or ES2023 if your Node.js version supports it (Node.js 18+ supports ES2022).

-  "target": "es2021"
+  "target": "es2022"

11-12: Ensure test file exclusion patterns are comprehensive

Your current exclusion patterns are good, but consider adding .e2e-spec.ts for end-to-end tests.

  "include": ["src/**/*.ts"],
-  "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
+  "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.e2e-spec.ts"]
libs/core/domain-services/jest.config.ts (1)

8-8: Consider adding JSON to module file extensions

For comprehensive testing, especially when modules import JSON files, adding the 'json' extension is recommended.

-  moduleFileExtensions: ['ts', 'js', 'html'],
+  moduleFileExtensions: ['ts', 'js', 'html', 'json'],
apps/crypto-quote-monorepo/jest.config.ts (2)

8-8: Consider adding JSON to module file extensions

For comprehensive testing, especially when modules import JSON files, adding the 'json' extension is recommended.

-  moduleFileExtensions: ['ts', 'js', 'html'],
+  moduleFileExtensions: ['ts', 'js', 'html', 'json'],

9-9: Consider setting coverage thresholds

Setting coverage thresholds can help maintain code quality. You might want to add coverage thresholds to ensure adequate test coverage.

  coverageDirectory: '../../coverage/apps/crypto-quote-monorepo',
+  coverageThreshold: {
+    global: {
+      branches: 80,
+      functions: 80,
+      lines: 80,
+      statements: 80
+    }
+  },
libs/core/domain/src/lib/models/exceptions/quota-exception.ts (1)

1-13: Exception classes are well-designed but consider adding more context.

The custom exceptions extend appropriate NestJS exception types and provide clear error messages. This enables proper HTTP status codes (404 for QuotaNotFoundException and 410 for QuotaExpiredException).

Consider enhancing the exception classes to include additional context that might be useful for debugging or client handling:

 export class QuotaNotFoundException extends NotFoundException {
   constructor(id: string) {
-    super(`Quota with ID ${id} not found.`);
+    super(`Quota with ID ${id} not found.`, 'QUOTA_NOT_FOUND');
+    this.cause = { quotaId: id };
   }
 }

 export class QuotaExpiredException extends GoneException {
   constructor(id: string) {
-    super(`Quota with ID ${id} has expired.`);
+    super(`Quota with ID ${id} has expired.`, 'QUOTA_EXPIRED');
+    this.cause = { quotaId: id };
   }
 }

This would provide error codes for API clients and include the ID in the exception metadata for logging systems.

apps/crypto-quote-monorepo/project.json (1)

37-42: Test Target Configuration is Minimal but Acceptable.
The test target currently only sets "passWithNoTests": true. Confirm that this is a temporary setup and that test cases will be added in future commits as needed.

libs/core/domain-services/tsconfig.json (1)

6-6: Consider enabling strict mode for better type safety.

Setting "strict": false while enabling individual strict checks like noImplicitOverride and noImplicitReturns creates an inconsistent configuration. For better maintainability and type safety, consider setting "strict": true to enable all strict type checks.

-    "strict": false,
+    "strict": true,
libs/core/domain/tsconfig.json (1)

6-6: Consider enabling strict mode for domain models

The domain layer represents your core business logic and data models, which would benefit significantly from TypeScript's strict mode. Setting strict: true would enable additional compile-time checks that can prevent runtime errors and make your domain more robust.

-    "strict": false,
+    "strict": true,
libs/core/domain-services/src/lib/repositories/quota.repository.ts (1)

1-6: Good implementation of the repository pattern

The abstract QuotaRepository class provides a clean interface for data access operations, following the repository pattern. The asynchronous methods with appropriate return types are well-designed.

Consider adding more repository methods for completeness

Depending on your requirements, you might want to extend this interface with additional methods such as:

  • update(id: string, data: Partial<Quota>): Promise<void>
  • delete(id: string): Promise<void>
  • getByFromAndTo(from: string, to: string): Promise<Quota[]>
  • exists(id: string): Promise<boolean>

This would provide a more comprehensive repository interface for managing quota entities.

prisma/schema.prisma (1)

16-25: Robust Quota Model Definition
The new Quota model covers all necessary fields with appropriate default values for id and timestamp. One suggestion: since field names like from and to can be reserved keywords in some SQL dialects, double-check that they are properly escaped or consider renaming them if issues arise.

libs/nest/tsconfig.json (2)

1-12: Clear Base Configuration with Useful Compiler Options
This configuration correctly extends from the base TS configuration and applies practical compiler options (e.g., noImplicitOverride, noImplicitReturns). Consider re-evaluating "strict": false if tighter type safety can be achieved without impacting the build.


13-23: Empty "files" and "include" Arrays Are Likely Intentional
The empty "files" and "include" arrays suggest that file inclusion is managed through referenced configurations. Please ensure this pattern is consistent and documented across the monorepo to avoid confusion.

libs/core/use-cases/src/lib/get-quota/get-quota.ts (1)

21-23: Consider timezone handling for expiration check

The current implementation compares Date objects directly, which works in most cases but might lead to unexpected behavior if dealing with quotas across different timezones.

  private isQuotaExpired(quota: Quota): boolean {
-   return new Date() > quota.expiresAt;
+   return new Date().getTime() > quota.expiresAt.getTime();
  }
libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (3)

5-7: Remove unnecessary empty constructor comment

The empty comment /**/ in the constructor doesn't provide any value and should be removed.

  constructor(private readonly quotaRepository: QuotaRepository) {
-   /**/
  }

9-11: Avoid unnecessary await on return

When returning a Promise directly, there's no need to use await.

  public async create(data: Quota): Promise<void> {
-   return await this.quotaRepository.create(data);
+   return this.quotaRepository.create(data);
  }

13-15: Avoid unnecessary await on return

Same as above, the await is redundant when returning a Promise.

  public async get(id: string): Promise<Quota | null> {
-   return await this.quotaRepository.get(id);
+   return this.quotaRepository.get(id);
  }
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)

5-8: Remove unnecessary constructor

The constructor doesn't provide any value and can be removed.

-  constructor() {
-    /**/
-  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-8: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

README.md (1)

1-83: README now missing project-specific information

While this Nx workspace README provides excellent guidance for working with the monorepo tools, it has completely replaced the original project-specific documentation. Consider retaining some information about the actual project purpose and functionality in addition to the Nx workspace instructions.

Add a section at the beginning of the README that describes the project's purpose and main features before diving into the Nx workspace documentation.

libs/infrastructure/src/lib/repositories/quota/quota.repository.ts (1)

7-32: Add error handling for database operations

The repository methods don't include any explicit error handling for database operations. If the Prisma operations fail, errors will propagate to the calling code without any contextual information.

Consider adding try/catch blocks to handle potential database errors and provide more context:

public async create(data: Quota): Promise<void> {
+  try {
    await this.prisma.quota.create({
      data: {
        id: data.id,
        from: data.from,
        to: data.to,
        amount: data.amount,
        rate: data.rate,
        convertedAmount: data.convertedAmount,
        timestamp: data.timestamp,
        expiresAt: data.expiresAt,
      },
    });
+  } catch (error) {
+    throw new Error(`Failed to create quota: ${error.message}`);
+  }
}

public async get(id: string): Promise<Quota | null> {
+  try {
    const quota = await this.prisma.quota.findUnique({
      where: { id },
    });

    return quota ? plainToInstance(Quota, quota, { excludeExtraneousValues: true }) : null;
+  } catch (error) {
+    throw new Error(`Failed to retrieve quota with id ${id}: ${error.message}`);
+  }
}
apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (1)

8-13: Add error handling for PrismaClient initialization.

The prismaClientProvider creates a new PrismaClient instance without any error handling or connection management. While this works, consider adding error handling and proper connection lifecycle management for production environments.

const prismaClientProvider = {
  provide: PrismaClient,
  useFactory: () => {
-    return new PrismaClient();
+    try {
+      const prisma = new PrismaClient();
+      // Verify connection
+      prisma.$connect();
+      return prisma;
+    } catch (error) {
+      console.error('Failed to initialize Prisma client:', error);
+      throw error;
+    }
  },
};

Consider also adding an onModuleDestroy hook to properly close the Prisma connection when the application shuts down.

SPEC.md (1)

1-245: Well-structured requirements document with minor formatting issues.

The specification document clearly outlines the requirements for the API, explaining endpoints, expected behavior, and technical considerations. However, there are several formatting inconsistencies according to the static analysis reports.

Consider addressing these formatting issues:

  1. Add a language specifier to fenced code blocks (line 54)
  2. Remove trailing punctuation in headings (lines 129 and 160)
  3. Make bullet points consistent by adding proper spacing and punctuation
🧰 Tools
🪛 LanguageTool

[uncategorized] ~15-~15: Posible confusión. Considere aplicar la sugerencia.
Context: ...oncisa(deseable). - Pruebas unitarias y de integración (opcional). > **💡 Nota so...

(AI_ES_GGEC_REPLACEMENT_CONFUSION)


[typographical] ~17-~17: Mayúsculas y minúsculas recomendadas.
Context: ...ación (opcional). > 💡 Nota sobre la Estructura del Proyecto: > Este repositorio prop...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[typographical] ~17-~17: Mayúsculas y minúsculas recomendadas.
Context: ...). > 💡 Nota sobre la Estructura del Proyecto: > Este repositorio proporciona una e...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~17-~17: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...💡 Nota sobre la Estructura del Proyecto:** > Este repositorio proporciona una est...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[style] ~45-~45: Las cartas y los correos electrónicos empiezan con mayúscula después del saludo.
Context: ...: "ETH" } ``` - Campos: - amount: Monto a convertir. - from: Cód...

(ES_CASE_AFTER_SALUTATION)


[uncategorized] ~45-~45: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ..." } ``` - Campos: - amount: Monto a convertir. - from: Códig...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~46-~46: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... - amount: Monto a convertir. - from: Código de la moneda origen (Ej.: ARS...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[misspelling] ~52-~52: Posible error de ortografía.
Context: ...e Precios:** Obtener el valor de rate en tiempo real consultando una API exte...

(APOSTROFO_ACENTO)


[uncategorized] ~58-~58: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...&to={from} ``` > Importante: Si no se puede integrar la API real, s...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[misspelling] ~61-~61: Posible error de ortografía.
Context: ...mountmultiplicando elamountpor elrate` obtenido. 3. **Gestión de Timestamps...

(APOSTROFO_ACENTO)


[typographical] ~69-~69: Mayúsculas y minúsculas recomendadas.
Context: ...de la creación). 4. Registro de la Cotización: Almacenar en la base de datos...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[misspelling] ~72-~72: Posible error de ortografía.
Context: ...icador único. - Valores de from, to y amount. - Tasa de conversión (...

(APOSTROFO_ACENTO)


[typographical] ~108-~108: Mayúsculas y minúsculas recomendadas.
Context: ..." } ``` --- ### 2️⃣ Endpoint para Obtener una Cotización - Método y Ruta: `G...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[typographical] ~108-~108: Mayúsculas y minúsculas recomendadas.
Context: ... --- ### 2️⃣ Endpoint para Obtener una Cotización - Método y Ruta: GET /quote/:id ...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[typographical] ~110-~110: Mayúsculas y minúsculas recomendadas.
Context: ...ra Obtener una Cotización - Método y Ruta: GET /quote/:id - Proceso: - ...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~110-~110: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...btener una Cotización - Método y Ruta: GET /quote/:id - Proceso: - Re...

(AI_ES_GGEC_REPLACEMENT_PUNCTUATION)


[misspelling] ~125-~125: Posible error de ortografía.
Context: ... de la cotización. - Valores de from, to y amount. - Tasa de conversión (`rate...

(APOSTROFO_ACENTO)


[uncategorized] ~145-~145: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... inválido, la API debe retornar un error 401 Unauthorized. --- ## 💻 Front-End...

(AI_ES_GGEC_REPLACEMENT_PUNCTUATION)


[typographical] ~155-~155: Mayúsculas y minúsculas recomendadas.
Context: ...lizando Next.js que permita: - Crear Cotizaciones: Un formulario donde el usuario i...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[misspelling] ~156-~156: Posible error de ortografía.
Context: ...e el usuario ingrese amount, from y to para generar una cotización. - **Consul...

(APOSTROFO_ACENTO)


[typographical] ~157-~157: Mayúsculas y minúsculas recomendadas.
Context: ...a generar una cotización. - Consultar Cotizaciones: Un campo para ingresar el ID de ...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~162-~162: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...debe ser desarrollada utilizando Next.js - La interfaz debe integrarse con la API d...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~163-~163: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... debe integrarse con la API desarrollada - Su desarrollo es opcional para la aproba...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~170-~170: Probablemente falta una coma.
Context: ...so de herramientas de IA (como ChatGPT, GitHub Copilot, etc.) para el desarrollo de es...

(AI_ES_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~172-~172: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...DME qué herramientas de IA se utilizaron - Explicar brevemente cómo se aprovecharon...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~173-~173: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... cómo se aprovecharon estas herramientas - Asegurarse de entender y poder explicar ...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~174-~174: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... explicar todo el código generado por IA - Mantener un balance entre el código gene...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[typographical] ~197-~197: Mayúsculas y minúsculas recomendadas.
Context: ...iguración especial. - Dockerización (Opcional): Si dockerizas la aplicación, in...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[style] ~198-~198: Esta fórmula es casi siempre innecesaria. Se desaconseja su uso, salvo en contextos muy técnicos.
Context: ... la aplicación, incluye un Dockerfile y/o docker-compose.yml con instrucciones ...

(Y_O)


[typographical] ~207-~207: Mayúsculas y minúsculas recomendadas.
Context: ...nto y comprensión del mismo. - Buenas Prácticas: - Uso correcto de NestJS e inyecci...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~213-~213: Posible confusión. Considere aplicar la sugerencia.
Context: ...ación efectiva. - Pruebas unitarias y de integración para respaldar la funcional...

(AI_ES_GGEC_REPLACEMENT_CONFUSION)


[typographical] ~222-~222: Mayúsculas y minúsculas recomendadas.
Context: ...izaciones. --- ## 📦 Instrucciones de Entrega - Repositorio: - Antes de comen...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[misspelling] ~229-~229: Posible error de ortografía.
Context: ...r un archivo ZIP que incluya la carpeta .git para mantener el historial de commits. ...

(APOSTROFO_ACENTO)


[uncategorized] ~231-~231: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...ner el historial de commits. > Nota: Si eliges la opción del ZIP, asegúrate...

(AI_ES_GGEC_REPLACEMENT_OTHER)

🪛 markdownlint-cli2 (0.17.2)

54-54: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


129-129: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


160-160: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

apps/crypto-quote-monorepo/src/api/quota/quota.controller.ts (1)

8-9: Remove empty comment in constructor.

The empty comment /**/ in the constructor serves no purpose and should be removed.

constructor(private readonly createQuotaUseCase: CreateQuotaUseCase, private readonly getQuotaUseCase: GetQuotaUseCase) {
-    /**/
}
libs/core/use-cases/src/lib/create-quota/create-quota.ts (3)

9-19: The execute method uses non-null assertions which could be improved

The method uses non-null assertion operators (!) on lines 12 and 13. While this works since validation is performed first, type safety could be improved.

  public async execute(quota: Partial<Quota>): Promise<Quota> {
    this.validateQuota(quota);

-   const rate = await this.getExchangeRate(quota.from!, quota.to!);
-   const convertedAmount = this.calculateConvertedAmount(quota.amount!, rate);
+   // After validation, we can safely cast these properties
+   const { from, to, amount } = quota as Required<Pick<Quota, 'from' | 'to' | 'amount'>>;
+   const rate = await this.getExchangeRate(from, to);
+   const convertedAmount = this.calculateConvertedAmount(amount, rate);
    const completeQuota = this.buildQuota(quota, rate, convertedAmount);

    await this.quotaService.create(completeQuota);

    return completeQuota;
  }

21-28: Enhance validation with format and value checks

The validation only checks for existence of fields but not their format or values. Consider adding validation for:

  • Currency code formats
  • Negative or zero amounts
  • Supported currencies
  private validateQuota(quota: Partial<Quota>): void {
    if (!quota.from || !quota.to) {
      throw new Error('Both "from" and "to" fields are required.');
    }
+   // Validate currency format (usually 3 uppercase letters for ISO currency codes)
+   if (!/^[A-Z]{3}$/.test(quota.from) || !/^[A-Z]{3}$/.test(quota.to)) {
+     throw new Error('Currency codes must be in ISO format (e.g., USD, EUR).');
+   }
    if (!quota.amount) {
      throw new Error('"amount" field is required.');
    }
+   // Validate amount is positive
+   if (quota.amount <= 0) {
+     throw new Error('Amount must be a positive number.');
+   }
  }

39-52: Make expiration time configurable

The expiration time is hardcoded to 5 minutes, which might be better as a configurable value.

  private buildQuota(quota: Partial<Quota>, rate: number, convertedAmount: number): Quota {
    const timestamp = new Date();
+   // Extract expiration minutes from config or use default
+   const EXPIRATION_MINUTES = 5;

    return {
      id: uuidv4(),
      from: quota.from!,
      to: quota.to!,
      amount: quota.amount!,
      rate,
      convertedAmount,
      timestamp,
-     expiresAt: new Date(timestamp.getTime() + 5 * 60 * 1000),
+     expiresAt: new Date(timestamp.getTime() + EXPIRATION_MINUTES * 60 * 1000),
    };
  }
libs/nest/project.json (1)

1-9: Overall project configuration is well-structured.
The JSON file defines all the necessary properties for an Nx library, including the project name, schema, source root, project type, and tags. One minor note: the key "// targets" is used for an inline comment on line 7. While this might be acceptable if the JSON parser supports comments (e.g., JSONC), ensure that this doesn’t cause issues in environments expecting strict JSON.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c963c25 and 9b3a7bd.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (104)
  • .eslintrc.js (0 hunks)
  • .gitignore (2 hunks)
  • .prettierignore (1 hunks)
  • .prettierrc (1 hunks)
  • .vscode/settings.json (1 hunks)
  • README.md (1 hunks)
  • SPEC.md (1 hunks)
  • apps/crypto-quote-monorepo/eslint.config.mjs (1 hunks)
  • apps/crypto-quote-monorepo/jest.config.ts (1 hunks)
  • apps/crypto-quote-monorepo/project.json (1 hunks)
  • apps/crypto-quote-monorepo/src/api/app.module.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/dtos/quota.dto.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/quota.controller.spec.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/quota.controller.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/main.ts (1 hunks)
  • apps/crypto-quote-monorepo/tsconfig.app.json (1 hunks)
  • apps/crypto-quote-monorepo/tsconfig.json (1 hunks)
  • apps/crypto-quote-monorepo/tsconfig.spec.json (1 hunks)
  • apps/crypto-quote-monorepo/webpack.config.js (1 hunks)
  • docker-compose.yml (1 hunks)
  • eslint.config.mjs (1 hunks)
  • jest.config.ts (1 hunks)
  • jest.preset.js (1 hunks)
  • libs/core/domain-services/README.md (1 hunks)
  • libs/core/domain-services/eslint.config.mjs (1 hunks)
  • libs/core/domain-services/jest.config.ts (1 hunks)
  • libs/core/domain-services/project.json (1 hunks)
  • libs/core/domain-services/src/index.ts (1 hunks)
  • libs/core/domain-services/src/lib/repositories/quota.repository.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/quota.service.ts (1 hunks)
  • libs/core/domain-services/tsconfig.json (1 hunks)
  • libs/core/domain-services/tsconfig.lib.json (1 hunks)
  • libs/core/domain-services/tsconfig.spec.json (1 hunks)
  • libs/core/domain/README.md (1 hunks)
  • libs/core/domain/eslint.config.mjs (1 hunks)
  • libs/core/domain/jest.config.ts (1 hunks)
  • libs/core/domain/project.json (1 hunks)
  • libs/core/domain/src/index.ts (1 hunks)
  • libs/core/domain/src/lib/models/cryptomkt.models.ts (1 hunks)
  • libs/core/domain/src/lib/models/exceptions/quota-exception.ts (1 hunks)
  • libs/core/domain/src/lib/models/quota.models.ts (1 hunks)
  • libs/core/domain/tsconfig.json (1 hunks)
  • libs/core/domain/tsconfig.lib.json (1 hunks)
  • libs/core/domain/tsconfig.spec.json (1 hunks)
  • libs/core/use-cases/README.md (1 hunks)
  • libs/core/use-cases/eslint.config.mjs (1 hunks)
  • libs/core/use-cases/jest.config.ts (1 hunks)
  • libs/core/use-cases/project.json (1 hunks)
  • libs/core/use-cases/src/index.ts (1 hunks)
  • libs/core/use-cases/src/lib/create-quota/create-quota.ts (1 hunks)
  • libs/core/use-cases/src/lib/get-quota/get-quota.ts (1 hunks)
  • libs/core/use-cases/tsconfig.json (1 hunks)
  • libs/core/use-cases/tsconfig.lib.json (1 hunks)
  • libs/core/use-cases/tsconfig.spec.json (1 hunks)
  • libs/infrastructure/README.md (1 hunks)
  • libs/infrastructure/eslint.config.mjs (1 hunks)
  • libs/infrastructure/jest.config.ts (1 hunks)
  • libs/infrastructure/project.json (1 hunks)
  • libs/infrastructure/src/index.ts (1 hunks)
  • libs/infrastructure/src/lib/models/index.ts (1 hunks)
  • libs/infrastructure/src/lib/models/responses/cryptomkt.models.ts (1 hunks)
  • libs/infrastructure/src/lib/models/responses/quota.models.ts (1 hunks)
  • libs/infrastructure/src/lib/repositories/index.ts (1 hunks)
  • libs/infrastructure/src/lib/repositories/quota/quota.repository.ts (1 hunks)
  • libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1 hunks)
  • libs/infrastructure/src/lib/services/index.ts (1 hunks)
  • libs/infrastructure/tsconfig.json (1 hunks)
  • libs/infrastructure/tsconfig.lib.json (1 hunks)
  • libs/infrastructure/tsconfig.spec.json (1 hunks)
  • libs/nest/README.md (1 hunks)
  • libs/nest/eslint.config.mjs (1 hunks)
  • libs/nest/jest.config.ts (1 hunks)
  • libs/nest/project.json (1 hunks)
  • libs/nest/src/index.ts (1 hunks)
  • libs/nest/src/lib/constants/response-codes.constants.ts (1 hunks)
  • libs/nest/src/lib/constants/response-messages.constants.ts (1 hunks)
  • libs/nest/src/lib/filters/http-exception.filter.ts (1 hunks)
  • libs/nest/tsconfig.json (1 hunks)
  • libs/nest/tsconfig.lib.json (1 hunks)
  • libs/nest/tsconfig.spec.json (1 hunks)
  • libs/shared/README.md (1 hunks)
  • libs/shared/eslint.config.mjs (1 hunks)
  • libs/shared/jest.config.ts (1 hunks)
  • libs/shared/project.json (1 hunks)
  • libs/shared/src/index.ts (1 hunks)
  • libs/shared/tsconfig.json (1 hunks)
  • libs/shared/tsconfig.lib.json (1 hunks)
  • libs/shared/tsconfig.spec.json (1 hunks)
  • nest-cli.json (0 hunks)
  • nx.json (1 hunks)
  • package.json (1 hunks)
  • prisma/schema.prisma (1 hunks)
  • src/app.controller.spec.ts (0 hunks)
  • src/app.controller.ts (0 hunks)
  • src/app.module.ts (0 hunks)
  • src/app.service.ts (0 hunks)
  • src/main.ts (0 hunks)
  • test/app.e2e-spec.ts (0 hunks)
  • test/jest-e2e.json (0 hunks)
  • tsconfig.base.json (1 hunks)
  • tsconfig.build.json (0 hunks)
  • tsconfig.json (0 hunks)
💤 Files with no reviewable changes (11)
  • nest-cli.json
  • tsconfig.build.json
  • src/app.controller.ts
  • .eslintrc.js
  • src/app.module.ts
  • src/app.service.ts
  • src/app.controller.spec.ts
  • test/jest-e2e.json
  • test/app.e2e-spec.ts
  • src/main.ts
  • tsconfig.json
🧰 Additional context used
🧬 Code Definitions (11)
apps/crypto-quote-monorepo/src/api/app.module.ts (1)
apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (1)
  • Module (15-53)
libs/core/domain-services/src/lib/services/quota.service.ts (1)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
libs/core/domain-services/src/lib/repositories/quota.repository.ts (1)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)
libs/infrastructure/src/lib/models/responses/cryptomkt.models.ts (1)
  • CryptomktResponse (4-13)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (6)
apps/crypto-quote-monorepo/src/api/app.module.ts (1)
  • Module (4-9)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)
  • CryptomktService (5-28)
libs/infrastructure/src/lib/repositories/quota/quota.repository.ts (1)
  • PrismaQuotaRepository (7-32)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1)
  • QuotaServiceImpl (4-16)
libs/core/use-cases/src/lib/create-quota/create-quota.ts (1)
  • CreateQuotaUseCase (6-53)
libs/core/use-cases/src/lib/get-quota/get-quota.ts (1)
  • GetQuotaUseCase (4-24)
libs/core/use-cases/src/lib/get-quota/get-quota.ts (2)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
libs/core/domain/src/lib/models/exceptions/quota-exception.ts (2)
  • QuotaNotFoundException (3-7)
  • QuotaExpiredException (9-13)
libs/infrastructure/src/lib/repositories/quota/quota.repository.ts (2)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
libs/infrastructure/src/lib/models/responses/quota.models.ts (1)
  • GetQuotaResponse (3-27)
apps/crypto-quote-monorepo/src/api/quota/quota.controller.ts (3)
libs/core/use-cases/src/lib/create-quota/create-quota.ts (1)
  • CreateQuotaUseCase (6-53)
libs/core/use-cases/src/lib/get-quota/get-quota.ts (1)
  • GetQuotaUseCase (4-24)
apps/crypto-quote-monorepo/src/api/quota/dtos/quota.dto.ts (1)
  • CreateQuoteDto (3-16)
libs/infrastructure/src/lib/models/responses/cryptomkt.models.ts (1)
libs/core/domain/src/lib/models/cryptomkt.models.ts (1)
  • Cryptomkt (1-5)
libs/core/use-cases/src/lib/create-quota/create-quota.ts (2)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)
  • CryptomktService (5-28)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
🪛 LanguageTool
SPEC.md

[uncategorized] ~15-~15: Posible confusión. Considere aplicar la sugerencia.
Context: ...oncisa(deseable). - Pruebas unitarias y de integración (opcional). > **💡 Nota so...

(AI_ES_GGEC_REPLACEMENT_CONFUSION)


[typographical] ~17-~17: Mayúsculas y minúsculas recomendadas.
Context: ...ación (opcional). > 💡 Nota sobre la Estructura del Proyecto: > Este repositorio prop...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[typographical] ~17-~17: Mayúsculas y minúsculas recomendadas.
Context: ...). > 💡 Nota sobre la Estructura del Proyecto: > Este repositorio proporciona una e...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~17-~17: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...💡 Nota sobre la Estructura del Proyecto:** > Este repositorio proporciona una est...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[style] ~45-~45: Las cartas y los correos electrónicos empiezan con mayúscula después del saludo.
Context: ...: "ETH" } ``` - Campos: - amount: Monto a convertir. - from: Cód...

(ES_CASE_AFTER_SALUTATION)


[uncategorized] ~45-~45: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ..." } ``` - Campos: - amount: Monto a convertir. - from: Códig...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~46-~46: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... - amount: Monto a convertir. - from: Código de la moneda origen (Ej.: ARS...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[misspelling] ~52-~52: Posible error de ortografía.
Context: ...e Precios:** Obtener el valor de rate en tiempo real consultando una API exte...

(APOSTROFO_ACENTO)


[uncategorized] ~58-~58: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...&to={from} ``` > Importante: Si no se puede integrar la API real, s...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[misspelling] ~61-~61: Posible error de ortografía.
Context: ...mountmultiplicando elamountpor elrate` obtenido. 3. **Gestión de Timestamps...

(APOSTROFO_ACENTO)


[typographical] ~69-~69: Mayúsculas y minúsculas recomendadas.
Context: ...de la creación). 4. Registro de la Cotización: Almacenar en la base de datos...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[misspelling] ~72-~72: Posible error de ortografía.
Context: ...icador único. - Valores de from, to y amount. - Tasa de conversión (...

(APOSTROFO_ACENTO)


[typographical] ~108-~108: Mayúsculas y minúsculas recomendadas.
Context: ..." } ``` --- ### 2️⃣ Endpoint para Obtener una Cotización - Método y Ruta: `G...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[typographical] ~108-~108: Mayúsculas y minúsculas recomendadas.
Context: ... --- ### 2️⃣ Endpoint para Obtener una Cotización - Método y Ruta: GET /quote/:id ...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[typographical] ~110-~110: Mayúsculas y minúsculas recomendadas.
Context: ...ra Obtener una Cotización - Método y Ruta: GET /quote/:id - Proceso: - ...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~110-~110: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...btener una Cotización - Método y Ruta: GET /quote/:id - Proceso: - Re...

(AI_ES_GGEC_REPLACEMENT_PUNCTUATION)


[misspelling] ~125-~125: Posible error de ortografía.
Context: ... de la cotización. - Valores de from, to y amount. - Tasa de conversión (`rate...

(APOSTROFO_ACENTO)


[uncategorized] ~145-~145: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... inválido, la API debe retornar un error 401 Unauthorized. --- ## 💻 Front-End...

(AI_ES_GGEC_REPLACEMENT_PUNCTUATION)


[typographical] ~155-~155: Mayúsculas y minúsculas recomendadas.
Context: ...lizando Next.js que permita: - Crear Cotizaciones: Un formulario donde el usuario i...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[misspelling] ~156-~156: Posible error de ortografía.
Context: ...e el usuario ingrese amount, from y to para generar una cotización. - **Consul...

(APOSTROFO_ACENTO)


[typographical] ~157-~157: Mayúsculas y minúsculas recomendadas.
Context: ...a generar una cotización. - Consultar Cotizaciones: Un campo para ingresar el ID de ...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~162-~162: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...debe ser desarrollada utilizando Next.js - La interfaz debe integrarse con la API d...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~163-~163: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... debe integrarse con la API desarrollada - Su desarrollo es opcional para la aproba...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~170-~170: Probablemente falta una coma.
Context: ...so de herramientas de IA (como ChatGPT, GitHub Copilot, etc.) para el desarrollo de es...

(AI_ES_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~172-~172: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...DME qué herramientas de IA se utilizaron - Explicar brevemente cómo se aprovecharon...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~173-~173: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... cómo se aprovecharon estas herramientas - Asegurarse de entender y poder explicar ...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[uncategorized] ~174-~174: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ... explicar todo el código generado por IA - Mantener un balance entre el código gene...

(AI_ES_GGEC_REPLACEMENT_OTHER)


[typographical] ~197-~197: Mayúsculas y minúsculas recomendadas.
Context: ...iguración especial. - Dockerización (Opcional): Si dockerizas la aplicación, in...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[style] ~198-~198: Esta fórmula es casi siempre innecesaria. Se desaconseja su uso, salvo en contextos muy técnicos.
Context: ... la aplicación, incluye un Dockerfile y/o docker-compose.yml con instrucciones ...

(Y_O)


[typographical] ~207-~207: Mayúsculas y minúsculas recomendadas.
Context: ...nto y comprensión del mismo. - Buenas Prácticas: - Uso correcto de NestJS e inyecci...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[uncategorized] ~213-~213: Posible confusión. Considere aplicar la sugerencia.
Context: ...ación efectiva. - Pruebas unitarias y de integración para respaldar la funcional...

(AI_ES_GGEC_REPLACEMENT_CONFUSION)


[typographical] ~222-~222: Mayúsculas y minúsculas recomendadas.
Context: ...izaciones. --- ## 📦 Instrucciones de Entrega - Repositorio: - Antes de comen...

(AI_ES_GGEC_REPLACEMENT_CASING_UPPERCASE)


[misspelling] ~229-~229: Posible error de ortografía.
Context: ...r un archivo ZIP que incluya la carpeta .git para mantener el historial de commits. ...

(APOSTROFO_ACENTO)


[uncategorized] ~231-~231: Probablemente hay un error. Considere aplicar la sugerencia.
Context: ...ner el historial de commits. > Nota: Si eliges la opción del ZIP, asegúrate...

(AI_ES_GGEC_REPLACEMENT_OTHER)

🪛 markdownlint-cli2 (0.17.2)
SPEC.md

54-54: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


129-129: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


160-160: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

🪛 Biome (1.9.4)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts

[error] 6-8: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

🔇 Additional comments (100)
.gitignore (5)

8-10: Dependencies Section – Node Modules Ignored
The inclusion of node_modules in this section is standard and correct for avoiding dependency bloat in version control.


20-29: Miscellaneous Files Section – Log and Cache Entries
The added entries for caches, logs, and temporary files (like /.sass-cache, /connect.lock, /coverage, etc.) are well-selected for ignoring common development artifacts. Everything looks good here.


30-33: System Files Section – OS Artifacts
The explicit ignores for system files, such as .DS_Store and Thumbs.db, are appropriately added.


40-43: Docker Section – Log and Override Files
The new Docker-related entries (docker-compose.override.yml and *.log) appropriately prevent log clutter from Docker processes.


44-45: Nx Cache Directory Ignored
Adding .nx to ignore the Nx cache ensures that build artifacts from Nx are not committed. This is ideal for maintaining a clean repository state.

libs/shared/README.md (1)

1-8: New README for Shared Library – Clear and Concise
This introductory README provides essential context and testing instructions for the shared library. As the library evolves, consider expanding it with additional usage examples or architectural notes.

libs/core/domain/README.md (1)

1-8: New README for Domain Library – Informative Documentation
The README clearly states that the library was generated with Nx and provides straightforward instructions for running unit tests. It forms a solid starting point for developers working within the domain layer.

libs/nest/README.md (1)

1-8: New README for Nest Library – Effective Test Instructions
This README efficiently communicates that the library utilizes Nx and provides the necessary instructions (nx test nest) for running tests via Jest. The document is clear and useful for onboarding contributors.

jest.preset.js (1)

1-4: Jest Preset Configuration – Simple and Neat
The file cleanly imports and re-exports the Nx Jest preset. Ensure that @nx/jest/preset is kept up-to-date in your dependencies. Optionally, adding a brief comment explaining the preset usage can further aid future maintainers.

libs/infrastructure/README.md (3)

1-2: Header Clarity

The header “# infrastructure” correctly identifies the library.


3-4: Nx Generation Notice

The note “This library was generated with Nx.” is succinct and informs readers about the project scaffolding tool in use.


5-7: Unit Test Instructions

The directions to run unit tests using nx test infrastructure are clear and consistent with similar project libraries.

libs/core/use-cases/README.md (3)

1-2: Header Clarity

The header “# use-cases” properly clarifies the library’s focus.


3-4: Nx Generation Note

The statement “This library was generated with Nx.” is concise and useful for context.


5-7: Unit Test Command

The instruction to run tests via nx test use-cases is straightforward and mirrors the approach used in other libraries.

libs/core/use-cases/eslint.config.mjs (2)

1-1: Relative Import Verification

The import line using the relative path to ../../../eslint.config.mjs is appropriate. Just ensure that any future reorganization keeps this path valid.


3-3: Exporting Configuration

Exporting the ESLint configuration with [...baseConfig] is concise and effectively extends the base configuration.

libs/core/domain-services/README.md (3)

1-2: Library Identification

The header “# domain-services” immediately informs the reader of the library’s focus.


3-4: Nx Generation Information

Informing that the library was generated with Nx provides useful context for maintainers and contributors.


5-7: Test Execution Instructions

The instructions “Run nx test domain-services to execute the unit tests via Jest.” are clear and consistent with project standards.

libs/infrastructure/project.json (2)

1-4: Project Configuration Baseline

The initial configuration blocks—defining the project name, schema, and source root—are clearly specified and adhere to Nx configuration standards.


5-6: Project Type and Tags

Declaring the project type as "library" with an empty tags array is appropriate for this configuration.

libs/nest/src/index.ts (1)

1-1: Good export pattern for the HTTP exception filter.

This follows the standard barrel pattern for exposing the HTTP exception filter to consumers, promoting clean code organization.

libs/shared/src/index.ts (1)

1-1: Good export pattern for the shared module.

Consistent use of barrel exports makes the shared module easily accessible to other parts of the application.

libs/shared/eslint.config.mjs (1)

1-3: Appropriate ESLint configuration extension.

This follows the recommended pattern for extending base ESLint configurations in a monorepo, ensuring consistent linting rules across the codebase.

libs/core/domain/eslint.config.mjs (1)

1-3: Consistent ESLint configuration extension.
The file correctly imports the shared base configuration from the relative path and exports a spread copy to extend its rules. This approach ensures consistency across projects in the monorepo.

libs/core/use-cases/project.json (1)

1-9: Valid project configuration with a minor JSON comment caution.
This JSON file provides the expected metadata (name, schema, source root, project type, tags, and targets) for the "use-cases" library. Note that JSON does not officially support comments—even though many tools tolerate keys like "// targets". Ensure your tooling and parsers accept this syntax.

libs/core/domain-services/project.json (1)

1-9: Consistent project metadata for domain-services.
The file mirrors the structure of other project configuration files by providing clear metadata and an instructional comment about targets. As mentioned previously, verify that the non-standard comment key does not cause issues with your JSON parser.

libs/core/domain/project.json (1)

1-9: Uniform project configuration for the domain library.
This configuration is aligned with other libraries in the monorepo, ensuring consistency in how project metadata is defined. The inclusion of the comment key ("// targets") is useful for developers but make sure your tooling accepts it.

libs/nest/eslint.config.mjs (1)

1-3: Standardized ESLint configuration for Nest-related code.
By importing the base ESLint configuration from a relative path and exporting its spread version, this file maintains consistency with the ESLint setup across the project. No issues spotted.

libs/infrastructure/src/index.ts (1)

1-3: Consolidated Re-export Structure

This file correctly aggregates exports from the submodules (./lib/models, ./lib/repositories, and ./lib/services). This consolidation enables consumers to import entities from a single entry point, increasing modularity and ease of use.

libs/infrastructure/eslint.config.mjs (1)

1-3: Effective ESLint Configuration Extension

The file imports the base ESLint configuration and exports a spread copy, which allows for future customization while preserving the base settings. This approach is consistent and clean.

libs/infrastructure/src/lib/repositories/index.ts (1)

1-2: Streamlined Repository Export

Re-exporting all entities from the quota.repository module centralizes access to quota-related repository functionalities. This design improves code maintainability and simplifies import paths for consumers.

apps/crypto-quote-monorepo/src/api/app.module.ts (1)

1-10: Concise NestJS Module Definition

The AppModule is defined following standard NestJS practices by importing the QuotaModule and setting up module metadata. Although controllers and providers are currently empty, the file establishes a clear foundation for future expansion.

libs/core/domain-services/eslint.config.mjs (1)

1-3: Consistent ESLint Config Extension

The file mirrors other ESLint configuration files in the repository by importing a base configuration and then exporting a spread copy. This consistency aids in maintaining unified linting rules across the project.

.prettierignore (1)

1-5: LGTM! Standard Prettier ignore configuration.

The Prettier ignore configuration correctly excludes build artifacts and cache directories, following standard practices for modern JS/TS projects with Nx.

jest.config.ts (1)

1-6: LGTM! Modern Jest configuration using Nx best practices.

The Jest configuration correctly uses the async configuration pattern with getJestProjectsAsync() from Nx, which automatically discovers and configures all Jest projects in the monorepo.

apps/crypto-quote-monorepo/eslint.config.mjs (1)

1-3: LGTM! Modern ESLint flat config approach.

This configuration correctly uses the new ESLint flat config format and extends the base configuration, maintaining consistency across the monorepo.

If this specific app needs custom linting rules in the future, you can add them to this file while still inheriting from the base config.

libs/infrastructure/jest.config.ts (1)

1-10: Well-structured Jest configuration.

This Jest configuration follows best practices for an infrastructure library in an Nx monorepo. It correctly sets up the test environment, TypeScript transformation, and coverage directory.

libs/core/use-cases/src/index.ts (1)

1-2: Good use of barrel exports.

The barrel exports pattern provides a clean API for the use-cases module, making it easier to import these use cases throughout the application.

libs/infrastructure/src/lib/models/index.ts (1)

1-2: Good organization of model exports.

These exports follow best practices for organizing model files, making it easier to import the models throughout the application.

libs/core/domain-services/src/index.ts (1)

1-6: Well-structured barrel exports

The file follows good practices with clear section headers and appropriate exports. This barrel pattern provides a clean public API for the domain-services module.

libs/core/domain/src/index.ts (1)

1-3: Appropriate domain model exports

The barrel file properly exports all domain models and exceptions, providing a clean interface for other modules to access these entities.

libs/core/use-cases/jest.config.ts (1)

1-10: Jest configuration looks good

The Jest configuration is well-structured with appropriate settings for a TypeScript project:

  • Proper display name
  • Correct preset path
  • Node test environment
  • TypeScript transform configuration
  • Appropriate module extensions
  • Well-defined coverage directory

This aligns with standard Jest configurations in a monorepo structure.

libs/infrastructure/tsconfig.spec.json (1)

1-10: TypeScript test configuration is properly set up

The configuration correctly extends the base config and includes appropriate settings for Jest testing:

  • CommonJS module system (required for Jest)
  • Node module resolution
  • Proper type definitions
  • Correctly configured inclusion patterns for test files

This follows TypeScript best practices for testing configurations.

libs/nest/jest.config.ts (1)

1-10: Review: Jest Config for Nest Library
The configuration file correctly sets the displayName, preset, and testEnvironment, and its transform rule using ts-jest references the appropriate TypeScript configuration file. The module extensions and coverage directory are properly defined, ensuring consistency with the overall monorepo testing setup.

libs/shared/jest.config.ts (1)

1-10: Review: Jest Config for Shared Library
This Jest configuration follows the same clear structure as the Nest configuration. The display name, preset, transform, module file extensions, and coverage directory are all set appropriately, which should streamline testing across the monorepo.

libs/shared/tsconfig.spec.json (1)

1-10: Review: tsconfig.spec for Shared Library Tests
The TypeScript configuration for testing is well defined. It extends the base tsconfig.json and sets compiler options suitable for test files (e.g., module: "commonjs", moduleResolution: "node", and proper type definitions). The include array comprehensively captures test files and declaration files, which is ideal for ensuring proper compilation.

libs/core/domain/jest.config.ts (1)

1-10: Review: Jest Config for Core Domain Module
This configuration is consistent with the other Jest configs in the monorepo. Setting the displayName, using a relative path for the preset, and configuring the transform with ts-jest all meet the expected standards. The coverage directory is appropriately defined relative to this module's location.

apps/crypto-quote-monorepo/webpack.config.js (1)

1-20: Webpack configuration looks good

The webpack configuration is properly structured for an Nx Node.js application using TypeScript. The output path, plugins, and build options are correctly configured.

libs/infrastructure/src/lib/services/index.ts (1)

1-5: Service exports look good

The barrel file properly exports services from both custom and implementation directories, following good module organization practices.

apps/crypto-quote-monorepo/tsconfig.spec.json (1)

1-10: TypeScript testing configuration is appropriate

The configuration correctly extends the base config and sets up the proper environment for Jest testing with Node.js and TypeScript.

libs/nest/src/lib/filters/http-exception.filter.ts (2)

1-31: Well-structured exception filter with proper error handling

The HttpExceptionFilter implementation follows NestJS best practices by properly:

  • Using the @catch decorator to target HttpException
  • Extracting the response context and status
  • Handling different response types (string vs object)
  • Providing a structured error response with relevant details

23-29: Good structured error response format

The response format includes all necessary information: success flag, status code, message, details, and timestamp.

apps/crypto-quote-monorepo/tsconfig.app.json (1)

1-13: Good TypeScript configuration for a NestJS application

The TypeScript configuration is well-structured and includes the necessary settings for a NestJS application:

  • Extends the base configuration
  • Sets appropriate module system (commonjs) for Node.js
  • Includes Node.js types
  • Enables decorator metadata for NestJS
  • Targets ES2021 for modern Node.js features
  • Properly excludes test files
libs/core/domain-services/jest.config.ts (2)

1-10: Well-structured Jest configuration for domain-services library

The Jest configuration is properly set up with:

  • Appropriate display name
  • Proper preset path
  • Node.js test environment
  • TS-Jest transformer with correct tsconfig path
  • Comprehensive file extensions
  • Proper coverage directory path

5-7: Good transformer configuration for TypeScript files

Your configuration correctly uses ts-jest with a reference to the spec tsconfig.

apps/crypto-quote-monorepo/jest.config.ts (1)

1-10: Well-structured Jest configuration for the application

The Jest configuration is properly set up with:

  • Appropriate display name
  • Proper preset path
  • Node.js test environment
  • TS-Jest transformer with correct tsconfig path
  • Comprehensive file extensions
  • Proper coverage directory path
libs/core/domain-services/tsconfig.lib.json (1)

1-16: TypeScript configuration looks well-structured.

The configuration extends the base tsconfig and sets appropriate compiler options for a library:

  • Output directory is correctly set to the dist folder
  • Declaration files are enabled for library consumption
  • Node types are included
  • ES2021 target ensures modern JavaScript features
  • Various strict type checking flags improve code quality

This follows best practices for TypeScript library configuration in an Nx monorepo.

libs/shared/tsconfig.lib.json (1)

1-16: Configuration is consistent with domain-services library.

This shared library configuration mirrors the pattern used in domain-services while adjusting the output directory appropriately for its location in the project structure (using "../../dist/out-tsc" instead of "../../../dist/out-tsc").

The consistency in compiler options across libraries helps maintain uniform code quality standards throughout the monorepo.

libs/core/domain-services/src/lib/services/quota.service.ts (1)

1-6: Abstract service follows clean architecture principles.

The QuotaService abstract class establishes a clear contract for quota operations with two well-defined methods:

  • create: Stores a new quota
  • get: Retrieves a quota by ID

This follows the dependency inversion principle by defining the interface that concrete implementations must satisfy, which enables better testability and flexibility.

The use of the Quota domain model from @monorepo/core-domain correctly maintains the separation of concerns.

apps/crypto-quote-monorepo/project.json (3)

1-7: Project Configuration Structure is Well-Defined.
The JSON structure clearly defines the project properties—name, schema, sourceRoot, projectType, and tags.


8-19: Build Target Configuration Review.
The build target uses the nx:run-commands executor with a custom webpack-cli command. Double-check that the argument "node-env=production" meets your intended environment variable convention (typically, it might be "NODE_ENV=production").


20-36: Serve Target Configuration Looks Good.
The serve target is configured to depend on the build target and includes distinct configurations for development and production environments. Ensure that the buildTarget values accurately reflect your build configurations.

libs/infrastructure/tsconfig.json (4)

1-3: TypeScript Configuration File Setup is Appropriate.
The configuration properly extends the base TS settings, ensuring consistency throughout the repository.


4-11: Strictness Options Are Configured Intentionally.
While "strict": false is set, other safety flags (e.g., noImplicitOverride, noImplicitReturns) are enabled. Confirm that this mix of settings aligns with your project’s tolerance for strict type-checking.


12-15: Empty "files" and "include" Arrays.
Make sure that relying on project references and broader tsconfig inclusions meets your intended behavior for file inclusion during compilation.


15-23: References to Sub-Configurations are Set Up Correctly.
The references to tsconfig.lib.json and tsconfig.spec.json promote modularity and help manage different build targets.

.prettierrc (1)

1-10: Prettier Configuration is Comprehensive and Clear.
The file now specifies formatting options for trailing commas, tab width, tab usage, semicolons, bracket spacing, print width, and retains the singleQuote setting. This should enforce a consistent code style across the project.

libs/shared/tsconfig.json (4)

1-3: TypeScript Configuration for Shared Library is Consistent.
Extending from the base configuration ensures that settings remain uniform across different parts of the codebase.


4-12: Compiler Options are Set Uniformly.
The choice of the CommonJS module system along with the specified strictness options aligns with the overall project practices.


13-15: Empty "files" and "include" Arrays Require Verification.
Ensure that this configuration relies on project references for file inclusion, as explicitly empty arrays might lead to unexpected exclusion of files if not managed elsewhere.


15-23: Proper Reference Setup.
The references to tsconfig.lib.json and tsconfig.spec.json are correctly established to cover library and test-specific settings.

libs/core/use-cases/tsconfig.json (4)

1-3: Consistent TypeScript Configuration for Use-Cases.
The file properly extends the base configuration from the monorepo and establishes a solid foundation for use-case code with consistent compiler options.


4-12: Uniform Compiler Options Across Libraries.
The settings for module type and various strictness flags match those used in other parts of your monorepo, which is beneficial for consistency.


13-15: Review of Empty "files" and "include" Arrays.
Double-check that the absence of explicit file inclusion is intended, and that your build process fully resolves file paths via project references.


15-23: References Configuration is on Point.
The inclusion of references to tsconfig.lib.json and tsconfig.spec.json supports modular compilation and proper test configuration.

libs/nest/tsconfig.lib.json (1)

1-16: Looks good - correct TypeScript config for a library module.

The TypeScript configuration is properly set up with appropriate compile options for a library in an Nx workspace. It correctly extends the base config and enables important strict type-checking features.

tsconfig.base.json (1)

1-29: Well-structured base TypeScript configuration.

The configuration correctly sets up path mappings for monorepo libraries and defines appropriate compiler options. This provides a solid foundation for TypeScript across the project.

libs/infrastructure/tsconfig.lib.json (1)

1-16: Well-configured TypeScript settings for a library in a monorepo

This is a solid TypeScript configuration for a library module within a monorepo structure. The compiler options appropriately enable type safety features like strictNullChecks and noImplicitAny, which help catch potential bugs at compile time. The include/exclude patterns correctly separate source code from tests and configuration files.

The output directory path is consistent with conventional Nx monorepo structures, and enabling declarations is essential for a library that will be consumed by other modules.

libs/core/domain/tsconfig.json (1)

1-23: Good organization with references to specialized configs

The configuration is well-organized with empty files and include arrays at this level, delegating to the specialized configurations (tsconfig.lib.json and tsconfig.spec.json) for specific file inclusion patterns. This separation of concerns is a good practice in monorepo structures.

libs/core/domain/tsconfig.spec.json (1)

1-10: Appropriate test configuration setup

The test configuration is well set up for Jest-based testing in a TypeScript environment. It correctly includes Jest and Node.js type definitions and targets test files with appropriate patterns. The CommonJS module system is a good choice for Jest compatibility.

prisma/schema.prisma (2)

1-9: Schema File & Generator Configuration Look Good
The header comments are helpful and the Prisma generator is correctly configured with the JavaScript provider.


11-14: Datasource Configuration is Properly Set
The datasource block correctly sets up PostgreSQL with the DATABASE_URL environment variable. Ensure that the environment variable is securely managed in production.

libs/core/use-cases/tsconfig.spec.json (1)

1-10: Solid Testing TS Configuration
This configuration is tailored for the testing environment with the appropriate output directory, module system, and TypeScript types (jest, node). Verify that the "include" patterns accurately capture all intended test files.

libs/core/use-cases/tsconfig.lib.json (1)

1-16: Well-Configured Library TS Settings
The settings here are robust—enabling declaration generation and enforcing strict checks like strictNullChecks and noImplicitAny are excellent for library maintainability. Optionally, consider aligning these strict options with a global strict policy if it benefits consistency across the codebase.

libs/nest/tsconfig.spec.json (1)

1-10: Test-Specific Configuration is Structured Correctly
This TS config for tests inherits from the base configuration and correctly sets compiler options for Jest testing. Confirm that the "include" array encompasses exactly the intended test files.

libs/core/use-cases/src/lib/get-quota/get-quota.ts (1)

1-24: Well-structured use case implementation with appropriate error handling

The implementation follows clean architecture principles, with clear separation of concerns. It properly handles error cases for non-existent and expired quotas.

libs/core/domain-services/tsconfig.spec.json (1)

1-10: LGTM! Standard TypeScript test configuration

The configuration file correctly extends the base configuration and includes necessary settings for Jest testing. The outDir pointing to ../../../dist/out-tsc aligns with a monorepo structure, and the test file patterns are comprehensive.

libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1)

4-16: Good implementation of the QuotaService interface

The service properly implements the required methods and delegates to the repository as expected in a clean architecture pattern.

libs/core/domain/tsconfig.lib.json (1)

1-16: Well-structured TypeScript configuration for domain library

This configuration is properly set up for a TypeScript library in an Nx workspace with appropriate compiler options:

  • Extends the base tsconfig.json
  • Sets appropriate strict type checking flags
  • Configures proper output directory structure
  • Correctly excludes test files from the library build
libs/nest/src/lib/constants/response-codes.constants.ts (1)

1-6: Good standardization of response codes

The constants defined here provide a consistent way to reference error codes throughout the application. The naming convention is clear and follows best practices for constant naming.

apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (1)

15-52: The module implementation is clean and follows dependency injection patterns correctly.

The module structure is well-organized, with clear separation of concerns and proper dependency injection. The use of factory providers makes the dependencies explicit and testable.

libs/nest/src/lib/constants/response-messages.constants.ts (1)

1-19: The error message constants are well-defined and follow consistent naming patterns.

The constants provide a good foundation for standardized error handling across the application. Using a mapping object for HTTP status codes is a clean approach.

libs/core/use-cases/src/lib/create-quota/create-quota.ts (2)

1-7: LGTM: Imports and class structure follow best practices

The imports and class structure follow dependency injection principles, which is good for testability and maintainability.


35-37: LGTM: Simple and correct calculation method

The calculation method is simple and correct for the basic use case.

package.json (2)

8-19:

❓ Verification inconclusive

Verify dependency version compatibility

Several dependencies have very recent versions that might have limited testing or compatibility:

  • uuid@11.1.0 is quite recent
  • @prisma/client@6.5.0 and prisma@6.5.0 are very recent

Let's check the latest official versions for these packages:


🌐 Web query:

What is the latest stable version of uuid npm package in 2023?

💡 Result:

The latest stable version of the uuid npm package in 2023 was 9.0.1, released on September 12, 2023[3][9][14]. This version maintained compatibility with Node.js environments while introducing key changes from previous major versions:

Key characteristics of v9.0.1:

  • Removed Node.js 10.x support (EOL April 2022)[1]
  • Dropped IE 11 and Safari 10 compatibility[1]
  • Included native TypeScript support (no separate @types/uuid required)[3]
  • Supported multiple UUID versions (v1-v7) with RFC4122 compliance[3][5]

The subsequent major version 10.0.0 was released in June 2024[9], introducing newer UUID versions and additional helper functions like v1ToV6 and v6ToV1. However, in 2023 specifically, v9.x remained the latest stable series throughout the year[9][14].

For projects requiring long-term support, the maintainers follow a policy of supporting one End-of-Life Node.js LTS release[1], making version updates relatively frequent while maintaining backward compatibility within major versions.

Citations:


Action: Verify Compatibility for Recent Dependencies

The uuid package is currently pinned at ^11.1.0, which is significantly ahead of the latest stable version in 2023 (v9.0.1). This jump in major versions could introduce breaking changes or exhibit behaviors not fully vetted in the legacy ecosystem. In a similar vein, both @prisma/client and prisma at version 6.5.0 are very recent releases and may carry compatibility risks if not sufficiently validated with your project’s stack.

  • UUID: Confirm if the jump to v11.1.0 is intentional and supported by your runtime environment. Review breaking changes and ensure any adjustments required by the new version (e.g., native TypeScript support adaptations) are properly handled.
  • Prisma Packages: Double-check that any changes in behavior or API from these very recent versions have been thoroughly tested against your application's requirements.

Please ensure that additional regression testing is done to mitigate any issues related to these cutting-edge dependency updates.


38-50:

✅ Verification successful

TypeScript and ESLint versions are very recent

The project uses TypeScript 5.7.2 and ESLint 9.8.0, which are very recent versions that might have compatibility issues with other tools.

Let's check if these are stable versions:


🌐 Web query:

Is TypeScript 5.7.2 a stable release version? What is the latest stable TypeScript version?

💡 Result:

TypeScript 5.7.2 is a stable release version, as indicated by its presence in npm's version history and GitHub release notes[4][9]. However, the latest stable version as of March 30, 2025 is TypeScript 5.8.2, released in February 2025[1][10][11].

Key details:

  • TypeScript 5.8 became the current stable release with features like:
    • Optimized path normalization and program update handling
    • Support for require() of ESM modules in CommonJS projects[1]
  • The versioning progression shows:
    • 5.7.2 (Nov 2024) ➔ 5.8 Beta (March 2025) ➔ 5.8.2 (Stable)[1][9]
  • Upcoming development includes:
    • TypeScript 5.9 in development[1]
    • A native Go port of the TypeScript compiler planned for late 2025[3]

You can install the latest stable version using:

npm install -D typescript

or get nightly builds with:

npm install -D typescript@next

Citations:


Validated Version Stability – No Immediate Changes Required

After verifying with the latest public release information, we can confirm that:

  • TypeScript 5.7.2 is a stable release. Although the current latest stable version is TypeScript 5.8.2, there are no known compatibility issues with version 5.7.2.
  • ESLint 9.8.0 is also a stable release and does not seem to present compatibility risks.

No adjustments are necessary based on these versions, though you might consider upgrading to the latest stable releases in the future if you want to benefit from additional features or optimizations.

.vscode/settings.json (1)

1-9: VS Code settings file is clear and effective.
The configuration properly sets critical settings such as ignoring Git limit warnings, organizing imports on save, defining the default formatter, and enabling auto-format on save. The custom spell-check word list is a nice touch for project-specific terminology.

nx.json (1)

1-44: NX workspace configuration meets the new monorepo standards.
This file comprehensively defines the workspace schema, sets up named input groups (for both default and production environments), and includes explicit exclusions for legacy ESLint and test-related files. The inclusion of the nxCloudId and the configuration for the essential plugins (Webpack, ESLint, and Jest) should help streamline build, serve, lint, and test processes throughout the workspace.

Comment on lines 4 to 14
image: postgres:latest
container_name: postgres_nx
restart: always
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin
POSTGRES_DB: mydatabase
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security concern: Hardcoded database credentials

Using hardcoded credentials and the "latest" image tag can lead to security issues and unpredictable behavior.

 services:
   postgres:
-    image: postgres:latest
+    image: postgres:15.4
     container_name: postgres_nx
     restart: always
     environment:
-      POSTGRES_USER: admin
-      POSTGRES_PASSWORD: admin
-      POSTGRES_DB: mydatabase
+      POSTGRES_USER: ${POSTGRES_USER:-admin}
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-admin}
+      POSTGRES_DB: ${POSTGRES_DB:-cryptoquote_db}
     ports:
       - '5432:5432'
     volumes:
       - postgres_data:/var/lib/postgresql/data

Create a .env.example file with:

POSTGRES_USER=admin
POSTGRES_PASSWORD=changeme
POSTGRES_DB=cryptoquote_db
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
image: postgres:latest
container_name: postgres_nx
restart: always
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin
POSTGRES_DB: mydatabase
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
services:
postgres:
image: postgres:15.4
container_name: postgres_nx
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER:-admin}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-admin}
POSTGRES_DB: ${POSTGRES_DB:-cryptoquote_db}
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
Suggested change
image: postgres:latest
container_name: postgres_nx
restart: always
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin
POSTGRES_DB: mydatabase
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
POSTGRES_USER=admin
POSTGRES_PASSWORD=changeme
POSTGRES_DB=cryptoquote_db

Comment on lines +25 to +31
public async get(id: string): Promise<Quota | null> {
const quota = await this.prisma.quota.findUnique({
where: { id },
});

return quota ? plainToInstance(GetQuotaResponse, quota, { excludeExtraneousValues: true }) : null;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Type mismatch in get method implementation

The get method is declared to return Promise<Quota | null>, but it's returning an instance of GetQuotaResponse when a quota is found. This type inconsistency could cause issues for callers expecting a Quota object.

Correct the implementation to ensure type consistency. Either:

- return quota ? plainToInstance(GetQuotaResponse, quota, { excludeExtraneousValues: true }) : null;
+ return quota ? plainToInstance(Quota, quota, { excludeExtraneousValues: true }) : null;

Or update the interface return type to match your implementation.

Comment on lines 11 to 14
@Post()
public async create(@Body() data: CreateQuoteDto) {
return await this.createQuotaUseCase.execute(data);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling to the create method.

The create method lacks error handling for failures that might occur in the use case execution. Consider implementing try-catch blocks with appropriate error responses.

@Post()
public async create(@Body() data: CreateQuoteDto) {
-  return await this.createQuotaUseCase.execute(data);
+  try {
+    return await this.createQuotaUseCase.execute(data);
+  } catch (error) {
+    // Handle specific errors from the use case
+    if (error instanceof ValidationError) {
+      throw new BadRequestException(error.message);
+    }
+    // Re-throw other errors for global exception filter to handle
+    throw error;
+  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Post()
public async create(@Body() data: CreateQuoteDto) {
return await this.createQuotaUseCase.execute(data);
}
@Post()
public async create(@Body() data: CreateQuoteDto) {
try {
return await this.createQuotaUseCase.execute(data);
} catch (error) {
// Handle specific errors from the use case
if (error instanceof ValidationError) {
throw new BadRequestException(error.message);
}
// Re-throw other errors for global exception filter to handle
throw error;
}
}

Comment on lines 16 to 19
@Get('/:id')
public async get(@Param('id') id: string) {
return await this.getQuotaUseCase.execute(id);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix route parameter format and add error handling.

There are two issues to address:

  1. The route parameter has a leading slash which doesn't follow NestJS conventions
  2. The method lacks error handling for potential exceptions from the use case
-@Get('/:id')
+@Get(':id')
public async get(@Param('id') id: string) {
-  return await this.getQuotaUseCase.execute(id);
+  try {
+    return await this.getQuotaUseCase.execute(id);
+  } catch (error) {
+    if (error instanceof QuotaNotFoundException) {
+      throw new NotFoundException(error.message);
+    }
+    if (error instanceof QuotaExpiredException) {
+      throw new BadRequestException(error.message);
+    }
+    throw error;
+  }
}

Don't forget to import the necessary exception classes and the domain exceptions from the use case.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Get('/:id')
public async get(@Param('id') id: string) {
return await this.getQuotaUseCase.execute(id);
}
@Get(':id')
public async get(@Param('id') id: string) {
try {
return await this.getQuotaUseCase.execute(id);
} catch (error) {
if (error instanceof QuotaNotFoundException) {
throw new NotFoundException(error.message);
}
if (error instanceof QuotaExpiredException) {
throw new BadRequestException(error.message);
}
throw error;
}
}

Comment on lines +30 to +33
private async getExchangeRate(from: string, to: string): Promise<number> {
const data = await this.cryptomktService.get(from, to);
return parseFloat(data.price);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for exchange rate failures

The method doesn't handle potential errors for unsupported currency pairs or API failures.

  private async getExchangeRate(from: string, to: string): Promise<number> {
-   const data = await this.cryptomktService.get(from, to);
-   return parseFloat(data.price);
+   try {
+     const data = await this.cryptomktService.get(from, to);
+     if (!data?.price) {
+       throw new Error('Invalid response format: missing price property');
+     }
+     const rate = parseFloat(data.price);
+     if (isNaN(rate) || rate <= 0) {
+       throw new Error(`Invalid exchange rate: ${data.price}`);
+     }
+     return rate;
+   } catch (error) {
+     throw new Error(`Failed to get exchange rate for ${from} to ${to}: ${error.message}`);
+   }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async getExchangeRate(from: string, to: string): Promise<number> {
const data = await this.cryptomktService.get(from, to);
return parseFloat(data.price);
}
private async getExchangeRate(from: string, to: string): Promise<number> {
try {
const data = await this.cryptomktService.get(from, to);
if (!data?.price) {
throw new Error('Invalid response format: missing price property');
}
const rate = parseFloat(data.price);
if (isNaN(rate) || rate <= 0) {
throw new Error(`Invalid exchange rate: ${data.price}`);
}
return rate;
} catch (error) {
throw new Error(`Failed to get exchange rate for ${from} to ${to}: ${error.message}`);
}
}

"name": "@monorepo/source",
"version": "0.0.0",
"license": "MIT",
"scripts": {},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing build and test scripts

The scripts section is empty. How are common tasks like building, testing, and linting supposed to be run? In a monorepo structure, scripts are crucial for CI/CD pipelines.

  "scripts": {}

Consider adding standard scripts like:

  "scripts": {
+   "build": "nx build",
+   "test": "nx test",
+   "lint": "nx lint",
+   "start": "nx serve"
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"scripts": {},
"scripts": {
+ "build": "nx build",
+ "test": "nx test",
+ "lint": "nx lint",
+ "start": "nx serve"
},

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (48)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.spec.ts (2)

18-23: Consider adding error handling test case.

The current test only verifies the happy path. I recommend adding a test case for when the repository's create method throws an error.

it('should propagate errors from repository create method', async () => {
  const error = new Error('Database error');
  repository.create.mockRejectedValue(error);
  
  await expect(service.create(quotaMock)).rejects.toThrow(error);
  expect(repository.create).toHaveBeenCalledWith(quotaMock);
});

1-41: Consider adding edge case tests.

The tests cover the main functionality well, but consider adding tests for edge cases such as:

  1. Passing undefined or invalid values to the get method
  2. Testing behavior with malformed quota objects
it('should handle invalid ID input gracefully', async () => {
  // Test with undefined
  await service.get(undefined as unknown as string);
  expect(repository.get).toHaveBeenCalledWith(undefined);
  
  // Test with empty string
  await service.get('');
  expect(repository.get).toHaveBeenCalledWith('');
});
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)

22-25: Improve error handling with additional context

Error handling is too generic. When catching errors, include more context about the currencies being requested to improve debugging and traceability.

catch (error) {
-  console.error('Error fetching data:', error);
-  throw error;
+  console.error(`Error fetching exchange rate from=${from}, to=${to}:`, error);
+  throw new Error(`Failed to fetch exchange rate: ${error.message}`);
}
libs/core/domain/src/lib/models/auth-response.ts (1)

1-4: Consider typing expires_in as number

The expires_in property typically represents token expiration time in seconds according to OAuth 2.0 standards. Consider changing it to a number type for better type safety and consistency with standards.

export class AuthResponse {
  access_token: string;
-  expires_in: string;
+  expires_in: number;
}
libs/core/domain-services/src/lib/services/mocks/quota.service.mock.ts (1)

4-7: Mock implementation could be improved for better test safety

The current implementation uses any type casting which bypasses TypeScript's type safety. Consider returning more appropriate mock values to make your tests more reliable.

- create: (_data: Quota) => Promise.resolve() as any,
- get: (_id: string) => Promise.resolve() as any,
+ create: (_data: Quota) => Promise.resolve({..._data} as Quota),
+ get: (_id: string) => Promise.resolve({ id: _id } as Quota),
libs/core/domain-services/src/lib/services/mocks/user.service.mock.ts (1)

4-7: Mock implementation could be improved for better type safety

Similar to the quota service mock, this implementation bypasses TypeScript's type safety with any casting. Consider returning meaningful mock objects that match the expected return types of the real service.

- create: (_user: Partial<User>) => Promise.resolve() as any,
- findByEmail: (_email: string) => Promise.resolve() as any,
+ create: (_user: Partial<User>) => Promise.resolve({ id: 'mock-id', ..._user } as User),
+ findByEmail: (_email: string) => Promise.resolve(_email ? { id: 'mock-id', email: _email } as User : null),
apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.ts (1)

8-11: Consider adding API documentation

For better API documentation, consider adding OpenAPI/Swagger decorators to describe this DTO in generated API docs.

  @IsString()
  @IsNotEmpty({ message: 'Password is required' })
  @MinLength(6, { message: 'Password must be at least 6 characters long' })
+ @ApiProperty({
+   description: 'User password',
+   example: 'password123',
+   minLength: 6
+ })
  password: string;
apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.ts (1)

7-9: Consider enhancing password validation

While the minimum length validation is good, consider strengthening password security by adding complexity requirements (uppercase, lowercase, numbers, special chars) and a maximum length constraint to prevent potential DoS attacks.

@IsString()
@MinLength(6)
+@MaxLength(100)
+@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {
+  message: 'Password must contain uppercase, lowercase, and numbers or special characters',
+})
password: string;
apps/crypto-quote-monorepo/src/api/quota/quota.controller.spec.ts (1)

104-106: Consider reorganizing the existence test

The basic "should be defined" test is typically placed at the beginning of the test suite rather than at the end. Consider moving it to be the first test after the beforeEach setup.

  });

+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+
  describe('create', () => {
    it('should create a new quota successfully', async () => {
      const createQuoteDto = {
// ...later in the file...
  });

-  it('should be defined', () => {
-    expect(controller).toBeDefined();
-  });
});
libs/core/domain/src/lib/models/user.models.ts (3)

6-6: Add documentation to explain the purpose of empty extension classes.

The CreateUser class extending BaseUser without additional properties lacks documentation explaining its purpose. While it provides type safety, its intention isn't immediately clear.

+/**
+ * Represents user data needed for registration.
+ * Inherits all properties from BaseUser.
+ */
 export class CreateUser extends BaseUser {}

8-8: Add documentation to explain the purpose of empty extension classes.

The LoginUser class extending BaseUser without additional properties lacks documentation explaining its purpose. While it provides type safety, its intention isn't immediately clear.

+/**
+ * Represents user data needed for login.
+ * Inherits all properties from BaseUser.
+ */
 export class LoginUser extends BaseUser {}

10-12: Consider making the User ID property readonly.

The id property in the User class represents an immutable database identifier but is currently mutable. Consider making it readonly to prevent accidental modifications.

 export class User extends BaseUser {
-  id: string;
+  readonly id: string;
 }
apps/crypto-quote-monorepo/src/api/auth/auth.module.ts (4)

15-51: Add global exception filter to handle authentication errors.

The AuthModule doesn't include exception filters for handling authentication-related errors in a consistent way, which could lead to inconsistent API responses for login/register failures.

Consider adding an exception filter to standardize authentication error responses:

 @Module({
   imports: [],
   controllers: [AuthController],
   providers: [
     prismaClientProvider,
+    {
+      provide: APP_FILTER,
+      useClass: AuthExceptionFilter,
+    },
     {
       provide: UserRepository,
       useFactory: (prismaClient: PrismaClient) => new PrismaUserRepository(prismaClient),
       inject: [PrismaClient],
     },
     // ... remaining providers
   ],
 })
 export class AuthModule {}

And create a corresponding filter class to handle specific authentication errors.


36-41: Simplify useFactory implementations for better readability.

The current UseCase factory implementations are unnecessarily verbose. They can be simplified while maintaining the same functionality.

 {
   provide: CreateUserUseCase,
-  useFactory: (authService: AuthService) => {
-    return new CreateUserUseCase(authService);
-  },
+  useFactory: (authService: AuthService) => new CreateUserUseCase(authService),
   inject: [AuthService],
 },

43-48: Simplify useFactory implementations for better readability.

The current UseCase factory implementations are unnecessarily verbose. They can be simplified while maintaining the same functionality.

 {
   provide: GetUserUseCase,
-  useFactory: (authService: AuthService) => {
-    return new GetUserUseCase(authService);
-  },
+  useFactory: (authService: AuthService) => new GetUserUseCase(authService),
   inject: [AuthService],
 },

50-51: Export core auth services for reuse in other modules.

The AuthModule doesn't export any of its providers, which means other modules can't reuse the AuthService or repositories. This may lead to duplication.

 @Module({
   imports: [],
   controllers: [AuthController],
   providers: [
     // ... providers
   ],
+  exports: [
+    AuthService,
+    UserService,
+    UserRepository,
+  ],
 })
 export class AuthModule {}
libs/core/use-cases/src/lib/get-user/get-user.ts (1)

7-9: Add method documentation with JSDoc comments.

The execute method lacks documentation to explain its purpose, parameters, and return values. Adding JSDoc comments would improve code readability and maintainability.

+  /**
+   * Authenticates a user with the provided credentials
+   * @param user - The user login credentials
+   * @returns A promise containing the authentication response with access token
+   * @throws Error if credentials are invalid or authentication fails
+   */
   public async execute(user: LoginUser): Promise<AuthResponse> {
     return await this.authService.login(user.email, user.password);
   }
libs/core/use-cases/src/lib/get-quota/get-quota.spec.ts (3)

13-25: Replace time-dependent test with fixed timestamps.

The test creates a valid quota by adding time to the current timestamp, which could make tests flaky if execution time varies. Using fixed timestamps would make tests more deterministic.

   it('should get a valid quota successfully', async () => {
+    const futureDate = new Date('2099-12-31T23:59:59Z');
     const validQuota = {
       ...quotaMock,
-      expiresAt: new Date(Date.now() + 1000 * 60),
+      expiresAt: futureDate,
     };

     jest.spyOn(quotaServiceMock, 'get').mockResolvedValue(validQuota);

     const result = await useCase.execute('123');

     expect(result).toEqual(validQuota);
     expect(quotaServiceMock.get).toHaveBeenCalledWith('123');
   });

34-44: Replace time-dependent test with fixed timestamps.

Similar to the valid quota test, this test uses the current time to create an expired quota. Using fixed timestamps would make tests more deterministic.

   it('should throw QuotaExpiredException when quota is expired', async () => {
+    const pastDate = new Date('2020-01-01T00:00:00Z');
     const expiredQuota = {
       ...quotaMock,
-      expiresAt: new Date(Date.now() - 1000 * 60),
+      expiresAt: pastDate,
     };

     jest.spyOn(quotaServiceMock, 'get').mockResolvedValue(expiredQuota);

     await expect(useCase.execute('expired-id')).rejects.toThrow(QuotaExpiredException);
     expect(quotaServiceMock.get).toHaveBeenCalledWith('expired-id');
   });

13-44: Extract test constants to improve readability and maintenance.

The test uses magic strings for IDs and creates similar quota objects in multiple tests. Extracting these to constants would improve readability and maintainability.

 describe('GetQuotaUseCase', () => {
   let useCase: GetQuotaUseCase;
+  
+  // Test constants
+  const TEST_IDS = {
+    VALID: '123',
+    NON_EXISTENT: 'non-existent-id',
+    EXPIRED: 'expired-id'
+  };
+  
+  const FIXED_DATES = {
+    FUTURE: new Date('2099-12-31T23:59:59Z'),
+    PAST: new Date('2020-01-01T00:00:00Z')
+  };

   beforeEach(() => {
     jest.clearAllMocks();
     useCase = new GetQuotaUseCase(quotaServiceMock);
   });

   it('should get a valid quota successfully', async () => {
     const validQuota = {
       ...quotaMock,
-      expiresAt: new Date(Date.now() + 1000 * 60),
+      expiresAt: FIXED_DATES.FUTURE,
     };

     jest.spyOn(quotaServiceMock, 'get').mockResolvedValue(validQuota);

-    const result = await useCase.execute('123');
+    const result = await useCase.execute(TEST_IDS.VALID);

     expect(result).toEqual(validQuota);
-    expect(quotaServiceMock.get).toHaveBeenCalledWith('123');
+    expect(quotaServiceMock.get).toHaveBeenCalledWith(TEST_IDS.VALID);
   });

   it('should throw QuotaNotFoundException when quota does not exist', async () => {
     jest.spyOn(quotaServiceMock, 'get').mockResolvedValue(null);

-    await expect(useCase.execute('non-existent-id')).rejects.toThrow(QuotaNotFoundException);
-    expect(quotaServiceMock.get).toHaveBeenCalledWith('non-existent-id');
+    await expect(useCase.execute(TEST_IDS.NON_EXISTENT)).rejects.toThrow(QuotaNotFoundException);
+    expect(quotaServiceMock.get).toHaveBeenCalledWith(TEST_IDS.NON_EXISTENT);
   });

   it('should throw QuotaExpiredException when quota is expired', async () => {
     const expiredQuota = {
       ...quotaMock,
-      expiresAt: new Date(Date.now() - 1000 * 60),
+      expiresAt: FIXED_DATES.PAST,
     };

     jest.spyOn(quotaServiceMock, 'get').mockResolvedValue(expiredQuota);

-    await expect(useCase.execute('expired-id')).rejects.toThrow(QuotaExpiredException);
-    expect(quotaServiceMock.get).toHaveBeenCalledWith('expired-id');
+    await expect(useCase.execute(TEST_IDS.EXPIRED)).rejects.toThrow(QuotaExpiredException);
+    expect(quotaServiceMock.get).toHaveBeenCalledWith(TEST_IDS.EXPIRED);
   });
libs/core/use-cases/src/lib/create-user/create-user.spec.ts (2)

1-1: Consider separating imports for better clarity.

For improved code organization, consider separating model imports from mock imports. This makes it clearer where each entity is coming from, especially since User and authResponseMock likely come from different files within the @monorepo/core-domain package.

-import { authResponseMock, User } from '@monorepo/core-domain';
+import { User } from '@monorepo/core-domain';
+import { authResponseMock } from '@monorepo/core-domain';

30-47: Consider enhancing error testing.

While this test case properly verifies that a ConflictException is thrown when a user already exists, it would be more robust to also verify the error message content to ensure it provides clear feedback.

-    await expect(useCase.execute(input)).rejects.toThrow(ConflictException);
+    await expect(useCase.execute(input)).rejects.toThrow(
+      new ConflictException('User already exists')
+    );

Additionally, consider adding a test case for when authService.register fails, to ensure errors are properly propagated.

libs/infrastructure/src/lib/repositories/user/user.repository.ts (1)

5-41: Extract user mapping to reduce duplication.

Consider extracting the mapping between Prisma user entities and domain User objects to a helper method to reduce code duplication and ensure consistency.

 export class PrismaUserRepository implements UserRepository {
   constructor(private readonly prisma: PrismaClient) {}
 
+  private mapPrismaUserToDomainUser(user: any): User {
+    return {
+      id: user.id,
+      email: user.email,
+      password: user.password,
+    };
+  }
+
   public async create(user: User): Promise<User | null> {
     try {
       const createdUser = await this.prisma.user.create({
@@ -17,11 +24,7 @@
         },
       });
 
-      return {
-        id: createdUser.id,
-        email: createdUser.email,
-        password: createdUser.password,
-      };
+      return this.mapPrismaUserToDomainUser(createdUser);
     } catch (error) {
       console.error('Error creating user:', error);
       return null;
@@ -40,11 +43,7 @@
         return null;
       }
 
-      return {
-        id: user.id,
-        email: user.email,
-        password: user.password,
-      };
+      return this.mapPrismaUserToDomainUser(user);
     } catch (error) {
       console.error('Error finding user by email:', error);
       return null;
libs/core/domain/src/lib/models/mocks/user.models.mock.ts (1)

13-16: Consider securing password in test mocks

While these are test mocks, it's a good practice to avoid using patterns that could resemble real passwords (like "login123").

If these mocks are used in documentation or logs, consider using obviously fake passwords like "TEST_PASSWORD_NOT_REAL" to prevent any confusion with real credentials.

libs/infrastructure/src/lib/services/implementations/auth/auth.service.spec.ts (2)

22-25: Consider improving type safety in the mock setup

The userService mock uses as any for typing, which could hide TypeScript errors. Consider using a more type-safe approach by explicitly defining all required methods and properties of the interface.

-  userService = {
-    findByEmail: jest.fn(),
-    create: jest.fn(),
-  } as any;
+  userService = {
+    findByEmail: jest.fn(),
+    create: jest.fn(),
+  } as jest.Mocked<UserService>;

31-33: Consider adding verification for password validation test case

The tests mock bcrypt.compare to always return true, but it would be valuable to also test the scenario where password validation fails. This ensures complete coverage of the authentication logic.

+ it('should throw UnauthorizedException when password is invalid', async () => {
+   userService.findByEmail.mockResolvedValue(mockUser);
+   (bcrypt.compare as jest.Mock).mockImplementationOnce(() => Promise.resolve(false));
+
+   await expect(authService.login('test@example.com', 'wrong_password')).rejects.toThrow(UnauthorizedException);
+ });
libs/infrastructure/src/lib/services/implementations/user/user.service.ts (2)

7-9: Simplify promise chains by removing unnecessary await

Since the method is already returning a Promise, you can simplify it by removing the redundant await.

public async create(user: User): Promise<User | null> {
-  return await this.userRepository.create(user);
+  return this.userRepository.create(user);
}

11-13: Simplify promise chains by removing unnecessary await

Similar to the create method, you can simplify this by removing the redundant await.

public async findByEmail(email: string): Promise<User | null> {
-  return await this.userRepository.findByEmail(email);
+  return this.userRepository.findByEmail(email);
}
libs/infrastructure/src/lib/repositories/user/user.repository.spec.ts (3)

11-14: Initialize mockPrismaClient more explicitly in beforeEach

The current setup initializes prismaClient but it's not used in any of the test cases (each test creates its own mock). Consider using this initialized instance or remove it if not needed.

beforeEach(() => {
  prismaClient = new PrismaClient() as jest.Mocked<PrismaClient>;
+ prismaClient.user = {
+   create: jest.fn(),
+   findUnique: jest.fn()
+ } as any;
  repository = new PrismaUserRepository(prismaClient);
});

24-28: Reuse the repository instance from beforeEach

Instead of creating a new repository instance in each test, reuse the one created in beforeEach and update the mock implementation when needed.

-const mockPrismaClient = {
-  user: { create: jest.fn().mockResolvedValue(mockUser) },
-} as any;
-
-const repository = new PrismaUserRepository(mockPrismaClient);
+prismaClient.user.create.mockResolvedValue(mockUser);

30-30: Verify the returned user object

The test only checks that the method doesn't throw, but it should also verify that the returned user object matches the expected data.

-await expect(repository.create(mockUser)).resolves.not.toThrow();
+const result = await repository.create(mockUser);
+expect(result).toEqual(mockUser);
apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.test.ts (1)

4-51: Consider adding tests for additional validation scenarios.

The current tests cover basic validation requirements, but consider adding tests for:

  1. Empty string values
  2. Maximum length constraints (if applicable)
  3. Password complexity requirements (if implemented)

Also, there appears to be a discrepancy between this DTO and its usage in the controller tests, where a name field is included but not tested here.

apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (2)

11-14: Consider making HTTP timeout configurable.

The hardcoded 5000ms timeout might be too short for slow networks or when the external API is experiencing high load. Consider making this configurable via environment variables.

 HttpModule.register({
-  timeout: 5000,
+  timeout: parseInt(process.env.HTTP_TIMEOUT || '5000'),
   maxRedirects: 5,
 }),

18-21: Consider a singleton pattern for PrismaClient.

Creating a new PrismaClient instance for each module could lead to connection pool exhaustion in a production environment. Consider implementing it as a singleton or using NestJS's built-in singleton scope.

 {
   provide: PrismaClient,
-  useFactory: () => new PrismaClient(),
+  useFactory: () => {
+    // Ensure single instance across the application
+    if (!global.prisma) {
+      global.prisma = new PrismaClient();
+    }
+    return global.prisma;
+  },
 },
libs/core/domain-services/src/lib/services/auth.service.ts (2)

3-7: Consider adding JSDoc comments.

Adding JSDoc comments to describe the expected behavior, parameters, return values, and potential errors for each method would improve developer experience and code maintainability.

 export abstract class AuthService {
+  /**
+   * Retrieves a user by their email address
+   * @param email The email address of the user to retrieve
+   * @returns A Promise that resolves to the User if found, or null if not found
+   */
   abstract get(email: string): Promise<User | null>;
+  
+  /**
+   * Authenticates a user with their email and password
+   * @param email The user's email address
+   * @param password The user's password
+   * @returns A Promise that resolves to an AuthResponse containing access token information
+   * @throws Error if authentication fails
+   */
   abstract login(email: string, password: string): Promise<AuthResponse>;
+  
+  /**
+   * Registers a new user with the provided email and password
+   * @param email The new user's email address
+   * @param password The new user's password
+   * @returns A Promise that resolves to an AuthResponse containing access token information
+   * @throws Error if registration fails (e.g., email already exists)
+   */
   abstract register(email: string, password: string): Promise<AuthResponse>;
 }

3-7: Consider adding token refresh functionality.

For a complete authentication service, consider adding methods for token validation and refresh operations, which are common requirements in authentication systems.

 export abstract class AuthService {
   abstract get(email: string): Promise<User | null>;
   abstract login(email: string, password: string): Promise<AuthResponse>;
   abstract register(email: string, password: string): Promise<AuthResponse>;
+  abstract validateToken(token: string): Promise<User>;
+  abstract refreshToken(refreshToken: string): Promise<AuthResponse>;
 }
libs/core/use-cases/src/lib/create-quota/create-quota.spec.ts (2)

13-37: Consider enhancing timestamp validation in the success test.

The test properly verifies the created quota's fields and service calls, but could be improved by checking that the expiration time is correctly set to 5 minutes after the timestamp.

 expect(result.id).toBeDefined();
 expect(result.timestamp).toBeDefined();
 expect(result.expiresAt).toBeDefined();
+// Verify expiresAt is 5 minutes after timestamp
+expect(result.expiresAt.getTime() - result.timestamp.getTime()).toBe(5 * 60 * 1000);
 expect(cryptomktServiceMock.get).toHaveBeenCalledWith('BTC', 'CLP');

39-64: Consider using parameterized tests for error cases.

The three error test cases follow the same pattern and could be refactored to a more concise parameterized approach to improve maintainability.

-it('should throw error when missing from field', async () => {
-  const input = {
-    to: 'CLP',
-    amount: 1,
-  };
-
-  await expect(useCase.execute(input)).rejects.toThrow('Both "from" and "to" fields are required.');
-});
-
-it('should throw error when missing to field', async () => {
-  const input = {
-    from: 'BTC',
-    amount: 1,
-  };
-
-  await expect(useCase.execute(input)).rejects.toThrow('Both "from" and "to" fields are required.');
-});
-
-it('should throw error when missing amount field', async () => {
-  const input = {
-    from: 'BTC',
-    to: 'CLP',
-  };
-
-  await expect(useCase.execute(input)).rejects.toThrow('"amount" field is required.');
-});
+test.each([
+  { 
+    name: 'missing from field', 
+    input: { to: 'CLP', amount: 1 }, 
+    expectedError: 'Both "from" and "to" fields are required.' 
+  },
+  { 
+    name: 'missing to field', 
+    input: { from: 'BTC', amount: 1 }, 
+    expectedError: 'Both "from" and "to" fields are required.' 
+  },
+  { 
+    name: 'missing amount field', 
+    input: { from: 'BTC', to: 'CLP' }, 
+    expectedError: '"amount" field is required.' 
+  }
+])('should throw error when $name', async ({ input, expectedError }) => {
+  await expect(useCase.execute(input)).rejects.toThrow(expectedError);
+});
prisma/schema.prisma (1)

16-25: Consider adding indexes to improve query performance.

The Quota model looks good functionally, but should include indexes on fields that will likely be used in queries, particularly timestamp and expiresAt.

model Quota {
  id              String   @id @default(uuid())
  from            String
  to              String
  amount          Float
  rate            Float
  convertedAmount Float
  timestamp       DateTime @default(now())
  expiresAt       DateTime
+
+  @@index([timestamp])
+  @@index([expiresAt])
}
libs/core/domain/src/index.ts (1)

1-12: Consider using named exports to prevent potential naming conflicts.

The barrel file uses wildcard exports (export *), which could lead to naming conflicts if two different files export entities with the same name. Consider using named exports to make imports more explicit and prevent conflicts.

// Models
-export * from './lib/models/auth-response';
-export * from './lib/models/cryptomkt.models';
-export * from './lib/models/exceptions/quota-exception';
-export * from './lib/models/quota.models';
-export * from './lib/models/user.models';
+export { AuthResponse } from './lib/models/auth-response';
+export { Cryptomkt } from './lib/models/cryptomkt.models';
+export { QuotaException } from './lib/models/exceptions/quota-exception';
+export { Quota } from './lib/models/quota.models';
+export { User, CreateUser } from './lib/models/user.models';

// Mocks
-export * from './lib/models/mocks/auth-response.models.mock';
-export * from './lib/models/mocks/cryptomkt.models.mock';
-export * from './lib/models/mocks/quota.models.mock';
-export * from './lib/models/mocks/user.models.mock';
+export { authResponseMock } from './lib/models/mocks/auth-response.models.mock';
+export { cryptomktMock } from './lib/models/mocks/cryptomkt.models.mock';
+export { quotaMock } from './lib/models/mocks/quota.models.mock';
+export { userMock, createUserMock } from './lib/models/mocks/user.models.mock';
libs/core/use-cases/src/lib/create-user/create-user.ts (1)

8-16: Remove unnecessary await and add try-catch for error handling.

There's an unnecessary await keyword in the return statement since this.authService.register is already returning a Promise. Also, consider adding a try-catch block to handle potential errors from the external service calls.

public async execute(user: CreateUser): Promise<AuthResponse> {
-  const existingUser = await this.authService.get(user.email);
-
-  if (existingUser) {
-    throw new ConflictException('User already exists');
-  }
-
-  return await this.authService.register(user.email, user.password);
+  try {
+    const existingUser = await this.authService.get(user.email);
+
+    if (existingUser) {
+      throw new ConflictException('User already exists');
+    }
+
+    return this.authService.register(user.email, user.password);
+  } catch (error) {
+    if (error instanceof ConflictException) {
+      throw error;
+    }
+    // Re-throw with appropriate error handling or logging
+    throw new Error(`Failed to create user: ${error.message}`);
+  }
}
libs/infrastructure/src/lib/services/implementations/auth/auth.service.ts (2)

12-19: Check for unset JWT secret in production environments
Reading the JWT secret directly from process.env['JWT_SECRET'] may lead to runtime errors if it's not set correctly. Consider adding validation logic or a configuration service to ensure the variable is defined in production.


25-33: Add logging or extra context when throwing UnauthorizedException
When user credentials fail validation, it may be useful to log authentication attempts for monitoring or security reasons. Consider adding a log statement (ensuring no sensitive data is exposed) before throwing the UnauthorizedException.

libs/infrastructure/src/lib/repositories/quota/quota.repository.spec.ts (1)

70-120: Robust testing of get method behavior
All relevant outcomes (found, not found, database error) are tested, ensuring high reliability. Consider adding edge-case tests for invalid or empty IDs if applicable.

libs/infrastructure/src/lib/services/implementations/user/user.service.spec.ts (2)

18-36: Consider testing error-throwing scenarios.

In addition to checking a null return value when creation fails, it would be beneficial to add a test that verifies behavior when userRepository.create throws an error (e.g., an unexpected database exception). This helps ensure robust error handling.


38-56: Expand coverage for error conditions.

Similar to the create method tests, consider adding a scenario where findByEmail throws an error (e.g., unexpected repository failure) or receives invalid parameters. This would further validate the service’s resilience.

apps/crypto-quote-monorepo/src/guards/jwt-auth.guard.ts (1)

9-13: Leverage Nest's module-based injection for JwtService.

Instead of instantiating JwtService directly, consider using Nest's JwtModule.register(...) and injecting it via the constructor. This approach centralizes configuration and takes advantage of Nest’s dependency injection patterns.

Example snippet illustrating a more idiomatic approach:

-  constructor() {
-    this.jwtService = new JwtService({
-      secret: process.env.JWT_SECRET,
-    });
-  }

+  constructor(private readonly jwtService: JwtService) {}
package.json (1)

16-17: Redundant bcrypt libraries

You're including both bcrypt and bcryptjs libraries which serve the same purpose. This adds unnecessary dependencies to your project.

-    "bcrypt": "^5.1.1",
-    "bcryptjs": "^3.0.2",
+    "bcrypt": "^5.1.1",

Choose one based on your requirements - bcrypt is typically faster but requires native compilation, while bcryptjs is a pure JavaScript implementation with no compilation required but slower performance.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 9b3a7bd and 11ab8df.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (55)
  • apps/crypto-quote-monorepo/src/api/app.module.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/auth.controller.spec.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/auth.controller.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/auth.module.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.test.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.test.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/quota.controller.spec.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/quota.controller.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (1 hunks)
  • apps/crypto-quote-monorepo/src/guards/jwt-auth.guard.ts (1 hunks)
  • libs/core/domain-services/src/index.ts (1 hunks)
  • libs/core/domain-services/src/lib/repositories/user.repository.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/auth.service.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/cryptomkt.service.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/mocks/auth.service.mock.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/mocks/cryptomkt.service.mock.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/mocks/quota.service.mock.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/mocks/user.service.mock.ts (1 hunks)
  • libs/core/domain-services/src/lib/services/user.service.ts (1 hunks)
  • libs/core/domain/src/index.ts (1 hunks)
  • libs/core/domain/src/lib/models/auth-response.ts (1 hunks)
  • libs/core/domain/src/lib/models/mocks/auth-response.models.mock.ts (1 hunks)
  • libs/core/domain/src/lib/models/mocks/cryptomkt.models.mock.ts (1 hunks)
  • libs/core/domain/src/lib/models/mocks/quota.models.mock.ts (1 hunks)
  • libs/core/domain/src/lib/models/mocks/user.models.mock.ts (1 hunks)
  • libs/core/domain/src/lib/models/user.models.ts (1 hunks)
  • libs/core/use-cases/src/index.ts (1 hunks)
  • libs/core/use-cases/src/lib/create-quota/create-quota.spec.ts (1 hunks)
  • libs/core/use-cases/src/lib/create-quota/create-quota.ts (1 hunks)
  • libs/core/use-cases/src/lib/create-user/create-user.spec.ts (1 hunks)
  • libs/core/use-cases/src/lib/create-user/create-user.ts (1 hunks)
  • libs/core/use-cases/src/lib/get-quota/get-quota.spec.ts (1 hunks)
  • libs/core/use-cases/src/lib/get-user/get-user.spec.ts (1 hunks)
  • libs/core/use-cases/src/lib/get-user/get-user.ts (1 hunks)
  • libs/infrastructure/src/lib/constants/auth.constants.ts (1 hunks)
  • libs/infrastructure/src/lib/constants/cryptomkt.constants.ts (1 hunks)
  • libs/infrastructure/src/lib/interfaces/jwt-payload.ts (1 hunks)
  • libs/infrastructure/src/lib/repositories/index.ts (1 hunks)
  • libs/infrastructure/src/lib/repositories/quota/quota.repository.spec.ts (1 hunks)
  • libs/infrastructure/src/lib/repositories/user/user.repository.spec.ts (1 hunks)
  • libs/infrastructure/src/lib/repositories/user/user.repository.ts (1 hunks)
  • libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.spec.ts (1 hunks)
  • libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/auth/auth.service.spec.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/auth/auth.service.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/quota/quota.service.spec.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/user/user.service.spec.ts (1 hunks)
  • libs/infrastructure/src/lib/services/implementations/user/user.service.ts (1 hunks)
  • libs/infrastructure/src/lib/services/index.ts (1 hunks)
  • package.json (1 hunks)
  • prisma/schema.prisma (1 hunks)
  • tsconfig.base.json (1 hunks)
✅ Files skipped from review due to trivial changes (7)
  • libs/infrastructure/src/lib/constants/auth.constants.ts
  • libs/core/domain-services/src/lib/services/mocks/cryptomkt.service.mock.ts
  • libs/core/domain/src/lib/models/mocks/cryptomkt.models.mock.ts
  • libs/core/domain/src/lib/models/mocks/quota.models.mock.ts
  • libs/core/domain/src/lib/models/mocks/auth-response.models.mock.ts
  • libs/infrastructure/src/lib/constants/cryptomkt.constants.ts
  • libs/core/domain-services/src/lib/services/mocks/auth.service.mock.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • tsconfig.base.json
  • libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts
  • libs/core/use-cases/src/lib/create-quota/create-quota.ts
🧰 Additional context used
🧬 Code Definitions (27)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.spec.ts (2)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1)
  • QuotaServiceImpl (4-14)
libs/core/domain/src/lib/models/mocks/quota.models.mock.ts (1)
  • quotaMock (3-12)
libs/core/domain-services/src/lib/services/mocks/user.service.mock.ts (1)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.test.ts (1)
apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.ts (1)
  • LoginUserDto (3-12)
libs/infrastructure/src/lib/services/implementations/auth/auth.service.spec.ts (3)
libs/infrastructure/src/lib/services/implementations/auth/auth.service.ts (1)
  • AuthServiceImpl (9-66)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/core/domain/src/lib/models/mocks/auth-response.models.mock.ts (1)
  • authResponseMock (3-6)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.spec.ts (1)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)
  • CryptomktServiceImpl (8-27)
libs/core/use-cases/src/lib/create-user/create-user.spec.ts (4)
libs/core/use-cases/src/lib/create-user/create-user.ts (1)
  • CreateUserUseCase (5-17)
libs/core/domain-services/src/lib/services/mocks/auth.service.mock.ts (1)
  • authServiceMock (3-7)
libs/core/domain/src/lib/models/mocks/auth-response.models.mock.ts (1)
  • authResponseMock (3-6)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/infrastructure/src/lib/repositories/user/user.repository.spec.ts (2)
libs/infrastructure/src/lib/repositories/user/user.repository.ts (1)
  • PrismaUserRepository (5-41)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/core/domain-services/src/lib/services/user.service.ts (1)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/infrastructure/src/lib/repositories/user/user.repository.ts (1)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/core/domain-services/src/lib/repositories/user.repository.ts (1)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/core/use-cases/src/lib/get-user/get-user.spec.ts (4)
libs/core/use-cases/src/lib/get-user/get-user.ts (1)
  • GetUserUseCase (4-10)
libs/core/domain-services/src/lib/services/mocks/auth.service.mock.ts (1)
  • authServiceMock (3-7)
libs/core/domain/src/lib/models/mocks/auth-response.models.mock.ts (1)
  • authResponseMock (3-6)
libs/core/domain/src/lib/models/mocks/user.models.mock.ts (1)
  • loginUserMock (13-16)
apps/crypto-quote-monorepo/src/api/auth/auth.module.ts (6)
apps/crypto-quote-monorepo/src/api/app.module.ts (1)
  • Module (5-10)
libs/infrastructure/src/lib/repositories/user/user.repository.ts (1)
  • PrismaUserRepository (5-41)
libs/infrastructure/src/lib/services/implementations/user/user.service.ts (1)
  • UserServiceImpl (4-14)
libs/infrastructure/src/lib/services/implementations/auth/auth.service.ts (1)
  • AuthServiceImpl (9-66)
libs/core/use-cases/src/lib/create-user/create-user.ts (1)
  • CreateUserUseCase (5-17)
libs/core/use-cases/src/lib/get-user/get-user.ts (1)
  • GetUserUseCase (4-10)
libs/core/use-cases/src/lib/create-quota/create-quota.spec.ts (4)
libs/core/use-cases/src/lib/create-quota/create-quota.ts (1)
  • CreateQuotaUseCase (5-52)
libs/core/domain-services/src/lib/services/mocks/cryptomkt.service.mock.ts (1)
  • cryptomktServiceMock (3-5)
libs/core/domain-services/src/lib/services/mocks/quota.service.mock.ts (1)
  • quotaServiceMock (4-7)
libs/core/domain/src/lib/models/mocks/cryptomkt.models.mock.ts (1)
  • cryptomktMock (3-7)
libs/infrastructure/src/lib/services/implementations/user/user.service.ts (1)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.test.ts (1)
apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.ts (1)
  • CreateUserDto (3-10)
apps/crypto-quote-monorepo/src/api/quota/quota.controller.ts (3)
libs/core/use-cases/src/lib/create-quota/create-quota.ts (1)
  • CreateQuotaUseCase (5-52)
apps/crypto-quote-monorepo/src/api/quota/dtos/quota.dto.ts (1)
  • CreateQuoteDto (3-16)
libs/core/domain/src/lib/models/quota.models.ts (1)
  • Quota (1-10)
libs/core/use-cases/src/lib/create-user/create-user.ts (2)
libs/core/domain/src/lib/models/user.models.ts (1)
  • CreateUser (6-6)
libs/core/domain/src/lib/models/auth-response.ts (1)
  • AuthResponse (1-4)
libs/core/use-cases/src/lib/get-user/get-user.ts (2)
libs/core/domain/src/lib/models/user.models.ts (1)
  • LoginUser (8-8)
libs/core/domain/src/lib/models/auth-response.ts (1)
  • AuthResponse (1-4)
apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (5)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (1)
  • CryptomktServiceImpl (8-27)
libs/infrastructure/src/lib/repositories/quota/quota.repository.ts (1)
  • PrismaQuotaRepository (7-32)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.ts (1)
  • QuotaServiceImpl (4-14)
libs/core/use-cases/src/lib/create-quota/create-quota.ts (1)
  • CreateQuotaUseCase (5-52)
libs/core/use-cases/src/lib/get-quota/get-quota.ts (1)
  • GetQuotaUseCase (4-24)
libs/infrastructure/src/lib/services/implementations/auth/auth.service.ts (4)
libs/infrastructure/src/lib/constants/auth.constants.ts (1)
  • TOKEN_EXPIRATION (1-1)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/core/domain/src/lib/models/auth-response.ts (1)
  • AuthResponse (1-4)
libs/infrastructure/src/lib/interfaces/jwt-payload.ts (1)
  • JwtPayload (1-4)
libs/core/use-cases/src/lib/get-quota/get-quota.spec.ts (2)
libs/core/domain-services/src/lib/services/mocks/quota.service.mock.ts (1)
  • quotaServiceMock (4-7)
libs/core/domain/src/lib/models/mocks/quota.models.mock.ts (1)
  • quotaMock (3-12)
libs/core/domain-services/src/lib/services/auth.service.ts (2)
libs/core/domain/src/lib/models/user.models.ts (1)
  • User (10-12)
libs/core/domain/src/lib/models/auth-response.ts (1)
  • AuthResponse (1-4)
apps/crypto-quote-monorepo/src/api/auth/auth.controller.ts (5)
libs/core/use-cases/src/lib/create-user/create-user.ts (1)
  • CreateUserUseCase (5-17)
libs/core/use-cases/src/lib/get-user/get-user.ts (1)
  • GetUserUseCase (4-10)
apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.ts (1)
  • CreateUserDto (3-10)
libs/core/domain/src/lib/models/auth-response.ts (1)
  • AuthResponse (1-4)
apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.ts (1)
  • LoginUserDto (3-12)
libs/infrastructure/src/lib/services/implementations/user/user.service.spec.ts (2)
libs/infrastructure/src/lib/services/implementations/user/user.service.ts (1)
  • UserServiceImpl (4-14)
libs/core/domain/src/lib/models/mocks/user.models.mock.ts (1)
  • userMock (18-22)
libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (2)
libs/infrastructure/src/lib/models/responses/cryptomkt.models.ts (1)
  • CryptomktResponse (4-13)
libs/infrastructure/src/lib/constants/cryptomkt.constants.ts (1)
  • CRYPTOMKT_CONSTANTS (1-3)
apps/crypto-quote-monorepo/src/api/app.module.ts (2)
apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (1)
  • Module (9-49)
apps/crypto-quote-monorepo/src/api/auth/auth.module.ts (1)
  • Module (15-51)
libs/core/domain/src/lib/models/mocks/user.models.mock.ts (1)
libs/core/domain/src/lib/models/user.models.ts (4)
  • BaseUser (1-4)
  • CreateUser (6-6)
  • LoginUser (8-8)
  • User (10-12)
🔇 Additional comments (71)
libs/infrastructure/src/lib/services/implementations/quota/quota.service.spec.ts (5)

1-3: LGTM! Clean imports and proper organization.

The imports are well-organized, pulling in the necessary dependencies from the correct modules.


5-8: Clear test suite structure.

Good use of descriptive variable names and proper typing with Jest mocks.


9-16: Well-structured test setup.

The beforeEach setup is clean and follows best practices. You're correctly initializing the mock repository and injecting it into the service.


25-31: LGTM! Thorough testing of the successful case.

Good test structure that verifies both the return value and the correct parameters being passed to the repository.


33-39: Good error case coverage.

The test properly handles the case where a quota is not found. This ensures the service correctly passes through the repository's null response.

libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.ts (2)

17-20: Add null checks and error handling for data extraction

The current implementation assumes the data structure will always be as expected, but API responses can vary. This could lead to runtime errors if the response format changes.

Remember to include the robust error handling that was previously recommended to ensure resilience against unexpected API responses.


8-27: Good implementation of CryptomktService

The service has been properly implemented with dependency injection for HttpService and using constants for the API URL. Also great use of lastValueFrom instead of the deprecated toPromise() method.

libs/infrastructure/src/lib/interfaces/jwt-payload.ts (1)

1-4: Good JWT payload structure

The JWT payload interface follows best practices with clear property names and appropriate types. Including both email and subject (sub) fields provides sufficient information for authentication purposes.

apps/crypto-quote-monorepo/src/api/app.module.ts (1)

5-10: Well-structured root module

The AppModule follows NestJS best practices by importing the required feature modules (QuotaModule and AuthModule) and delegating functionality to them. The clean and minimal structure makes the application architecture clear.

libs/infrastructure/src/lib/repositories/index.ts (1)

1-2: LGTM: Clean, standard index barrel file

The index file is correctly exporting all entities from the quota and user repositories, making them accessible through a single import. This follows the barrel pattern which is a best practice in TypeScript project organization.

apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.ts (1)

3-12: LGTM: Well-defined DTO with proper validation

The LoginUserDto class is well-implemented with appropriate validation decorators from class-validator. The validation messages are clear and helpful for users.

libs/core/domain-services/src/lib/services/cryptomkt.service.ts (1)

1-5: Well-structured service interface

The abstract CryptomktService class provides a clean contract for crypto market data retrieval. The method signature is clear, properly typed, and follows the dependency inversion principle.

libs/core/use-cases/src/index.ts (1)

1-4: Good use of barrel exports for clean architecture

This barrel file properly exports use case modules, providing a clean public API that simplifies imports for consumers while maintaining separation of concerns.

apps/crypto-quote-monorepo/src/api/quota/quota.controller.spec.ts (1)

7-106: Previous issues addressed, test coverage is comprehensive

The test implementation properly mocks dependencies, includes functional tests for both success and failure cases, and follows NestJS testing best practices.

libs/core/use-cases/src/lib/get-quota/get-quota.spec.ts (2)

5-11: LGTM! The test setup is clean and follows best practices.

The test setup properly initializes the useCase with mocked dependencies and clears mocks between tests, which prevents test pollution.


27-32: LGTM! The error test case is well-structured.

This test correctly verifies that a QuotaNotFoundException is thrown when the quota doesn't exist. The assertion and mock verification are properly implemented.

libs/core/domain-services/src/lib/services/user.service.ts (1)

1-6: Well-designed service abstraction.

The abstract UserService class provides a clean contract for user operations with appropriately defined method signatures. Using Partial<User> for the create method allows flexibility when creating new users.

libs/core/domain-services/src/lib/repositories/user.repository.ts (1)

3-6:

❓ Verification inconclusive

Verify parameter type consistency with UserService.

The UserRepository.create method requires a complete User object, while UserService.create accepts a Partial<User>. Verify this is intentional, as implementations of UserService will need to transform partial user data into complete user objects before calling the repository.


🏁 Script executed:

#!/bin/bash
# Find any implementations of UserService to check how they handle this difference
echo "Looking for UserService implementations..."
rg -A 3 -B 3 "class \w+UserService (implements|extends) UserService" --type ts

Length of output: 175


Verify UserService conversion logic

The repository’s abstract method create requires a complete User object, while the corresponding service appears to accept Partial<User>. Our initial search for concrete UserService implementations did not yield any results to clarify how—or if—the transformation from partial to full user is handled. Please verify manually that any UserService implementation either transforms a Partial<User> into a full User before calling the repository or otherwise intentionally manages this discrepancy to avoid runtime issues.

libs/core/use-cases/src/lib/create-user/create-user.spec.ts (1)

14-28: Test case covers the happy path well.

This test case effectively verifies that the use case properly calls the auth service methods and returns the expected result for a successful user creation.

libs/core/use-cases/src/lib/get-user/get-user.spec.ts (2)

1-20: Well-structured test for happy path

The test effectively verifies the successful login scenario by:

  1. Properly mocking the auth service response
  2. Checking that the result matches expected data
  3. Verifying correct parameters are passed to the auth service

The test follows good practices with proper setup and clear assertions.


22-28: Good error case coverage

This test properly validates error handling by:

  1. Mocking a rejected promise with a specific error message
  2. Verifying the error is properly propagated through the use case
  3. Ensuring the auth service is still called with the correct parameters

Including both happy and error paths demonstrates thorough test coverage.

libs/infrastructure/src/lib/services/custom/cryptomkt/cryptomkt.service.spec.ts (3)

5-19: Good test setup with proper mocking

The test setup is well-structured with:

  1. Proper initialization of the service with mocked dependencies
  2. Clean teardown using jest.clearAllMocks() to prevent test pollution
  3. Complete HTTP service mock with both get and post methods

This provides a clean foundation for the test cases.


20-47: Comprehensive API success path testing

The test effectively validates the happy path by:

  1. Properly mocking the HTTP response with realistic data
  2. Verifying the response is transformed to the expected instance type
  3. Checking that all data fields are correctly mapped
  4. Confirming the HTTP service is called exactly once

This ensures the service correctly processes and transforms API responses.


49-55: Well-handled error case testing

The error case test properly:

  1. Mocks a failed HTTP request using rxjs throwError
  2. Verifies the service propagates the error
  3. Ensures the HTTP service is still called once

This ensures errors from the API are properly handled and propagated.

libs/core/domain/src/lib/models/mocks/user.models.mock.ts (1)

1-22: Well-structured mock objects

The mock objects follow best practices by:

  1. Providing distinct, easily identifiable test data for each user type
  2. Using clearly fake but realistic values (test emails and passwords)
  3. Properly implementing all required fields for each user type
  4. Organizing mocks logically with proper spacing

These mocks will be valuable for consistent testing across the application.

libs/infrastructure/src/lib/services/index.ts (1)

1-7: Well-organized barrel file with clear categorization

The index file effectively:

  1. Groups exports logically by Custom and Implementation categories
  2. Uses consistent export patterns
  3. Provides a clean, centralized access point for all services

This organization will improve maintainability and discoverability of services.

libs/infrastructure/src/lib/services/implementations/auth/auth.service.spec.ts (3)

38-53: LGTM! Good test coverage for login method

The test cases for the login method cover both successful authentication and the case where the user is not found, which is good practice for comprehensive test coverage.


55-73: LGTM! Good test coverage for register method

The test cases for the register method cover successful user creation and failure scenarios, which provides good test coverage for this functionality.


75-92: LGTM! Good test coverage for get method

The test cases for the get method cover both scenarios where a user is found and not found, ensuring the method behaves correctly in all scenarios.

libs/infrastructure/src/lib/services/implementations/user/user.service.ts (1)

4-14: LGTM! Clean implementation with proper dependency injection

The service implementation follows the dependency injection pattern and maintains a clean separation of concerns by delegating to the repository. The class correctly implements the UserService interface.

apps/crypto-quote-monorepo/src/api/auth/dtos/login-user.dto.test.ts (5)

4-12: LGTM! Well-structured positive test case

The test case for successful validation is well-structured and verifies that valid inputs pass validation without errors.


14-21: LGTM! Good validation for missing email

The test case properly verifies that validation fails when the email is missing and checks for the correct error message.


23-31: LGTM! Good validation for invalid email format

The test case properly verifies that validation fails when an invalid email format is provided and checks for the correct error message.


33-40: LGTM! Good validation for missing password

The test case properly verifies that validation fails when the password is missing and checks for the correct error message.


42-50: LGTM! Good validation for password length requirement

The test case properly verifies that validation fails when the password is too short and checks for the correct error message.

libs/infrastructure/src/lib/repositories/user/user.repository.spec.ts (3)

40-54: LGTM! Good error handling test for create method

The test case properly verifies that database errors are propagated when creating a user.


57-91: LGTM! Good test cases for findByEmail method

The test cases for findByEmail thoroughly cover the scenarios where a user is found and where no user exists, with proper assertions for each case.


93-101: LGTM! Good error handling test for findByEmail method

The test case properly verifies that database errors are propagated when finding a user by email.

apps/crypto-quote-monorepo/src/api/auth/dtos/create-user.dto.test.ts (5)

1-12: Well-structured validation test for the happy path.

The test correctly validates that a properly formatted email and password of sufficient length pass validation without errors. This confirms the DTO works as expected for valid inputs.


14-22: Appropriate validation test for email format.

The test properly verifies that the @IsEmail() decorator rejects invalid email formats and triggers the correct constraint error.


24-32: Good test for password length validation.

This test correctly verifies that the @MinLength(6) decorator rejects passwords that are too short and triggers the appropriate constraint error.


34-41: Comprehensive test for required email field.

This test appropriately checks that omitting the email field fails validation and triggers the expected constraint error.


43-50: Good test for required password field.

The test correctly verifies that omitting the password field fails validation and triggers the expected constraint error.

apps/crypto-quote-monorepo/src/api/auth/auth.controller.spec.ts (6)

1-15: Well-structured test setup with proper mocking.

The imports and mock setup are appropriate for testing the auth controller. The mock response correctly models the expected authentication response structure.


16-38: Good test module configuration with dependency injection.

The test module is correctly configured with mocked dependencies for the use cases, allowing isolated testing of the controller logic.


40-52: Effective test for successful user registration.

The test correctly verifies that the controller passes the DTO to the use case and returns the expected authentication response.


54-64: Good error handling test for registration.

The test properly verifies that errors from the use case are propagated through the controller.


67-78: Effective test for successful login.

The test correctly verifies that the controller passes login credentials to the use case and returns the expected authentication response.


80-89: Good error handling test for login.

The test properly verifies that authentication errors from the use case are propagated through the controller.

apps/crypto-quote-monorepo/src/api/quota/quota.module.ts (4)

1-8: Clear imports with appropriate dependencies.

The import statements correctly reference the necessary interfaces, use cases, and implementations from their respective modules.


9-15: Well-configured HTTP module.

The HttpModule is properly configured with timeout and redirect limits, which is good practice for external API calls.


16-46: Well-structured dependency injection.

The providers are properly set up with factory functions and dependency injection, following best practices for NestJS modules.


22-46: Clean service provider implementations.

The service providers are well-organized with clear factory functions and dependency injection. The module correctly wires up the use cases with their required dependencies.

libs/core/domain-services/src/lib/services/auth.service.ts (1)

1-7: Well-defined auth service interface.

The abstract class provides a clear contract for authentication operations with appropriate method signatures.

libs/core/use-cases/src/lib/create-quota/create-quota.spec.ts (2)

1-3: Imports look good.

All necessary dependencies are correctly imported from their respective modules.


5-11: Good test setup with proper initialization and cleanup.

The test suite has a proper setup with beforeEach to clear mocks and initialize the use case with its dependencies. This follows testing best practices.

prisma/schema.prisma (1)

7-14: Database configuration looks good.

The Prisma client generator and PostgreSQL datasource are correctly configured with environment variable for the database URL.

libs/core/use-cases/src/lib/create-user/create-user.ts (1)

1-7: Good class setup with proper dependency injection.

The imports and class constructor with dependency injection follow best practices.

libs/infrastructure/src/lib/services/implementations/auth/auth.service.ts (2)

35-44: Confirm consistent exception types for user creation failures
Registration currently throws UnauthorizedException if user creation fails. If the upstream use case already handles user existence checks, this might be consistent. Otherwise, consider a more specific exception if the failure reason is distinct from authorization issues.


55-65: Ensure token expiration aligns with security policies
The expires_in field is set to TOKEN_EXPIRATION (e.g., "1h"). Verify that this matches any security or compliance requirements for token lifetimes, or allow dynamic configuration if needed.

libs/infrastructure/src/lib/repositories/quota/quota.repository.spec.ts (1)

16-47: Comprehensive coverage of successful and failing create flows
These tests effectively confirm that the repository handles normal creation and database errors. This is a solid approach.

apps/crypto-quote-monorepo/src/api/auth/auth.controller.ts (2)

11-14: Verify error handling and response codes for registration
The register endpoint passes through the use case’s exception handling. Confirm that the resulting HTTP status codes match front-end expectations (e.g., 409 for user conflict).


16-19: Consistent pattern for login
The login flow is straightforward and mirrors best practices by delegating logic to a use case. This consistency helps maintain clarity across the codebase.

libs/infrastructure/src/lib/services/implementations/user/user.service.spec.ts (1)

5-16: Looks good!

The test setup is clear and straightforward. No issues found in this block.

apps/crypto-quote-monorepo/src/api/quota/quota.controller.ts (2)

13-15: Add error handling for create.

This is the same suggestion from a past review, recommending you catch potential exceptions from the use case and provide an appropriate HTTP response.


18-19: Remove the leading slash in the route parameter.

This is the same feedback provided previously regarding NestJS conventions. Typically, @Get(':id') is used without a slash.

libs/core/domain-services/src/index.ts (1)

1-15: Well-organized exports with clear categorization

The barrel file is well-structured with logical grouping of exports by their type (repositories, services, and mocks). This organization makes the codebase more maintainable and improves developer experience by providing clear import paths.

package.json (4)

5-5: Missing build and test scripts

The scripts section is empty. How are common tasks like building, testing, and linting supposed to be run? In a monorepo structure, scripts are crucial for CI/CD pipelines.

Consider adding standard scripts like:

  "scripts": {
+   "build": "nx build",
+   "test": "nx test",
+   "lint": "nx lint",
+   "start": "nx serve"
  }

25-25:

❌ Incorrect review comment

Potentially unstable UUID version

uuid version 11.1.0 is very recent and might introduce stability issues. Consider using a more established version.

-    "uuid": "^11.1.0"
+    "uuid": "^9.0.0"

🌐 Web query:

What is the latest stable version of the uuid npm package?

💡 Result:

The latest stable version of the uuid npm package is 11.1.0, released on February 19, 2025[1][4]. This version includes features like RFC9562 compliance, improved TypeScript integration, and support for modern crypto APIs[4][6].

Key details:

  • Version 11 introduced breaking changes, such as stricter type definitions and Uint8Array for binary UUIDs[4][6].
  • The package supports all UUID versions (1, 3, 4, 5, 6, 7) and works across Node.js, browsers, and React Native[4][5].
  • Install it using:
    npm install uuid  

Older versions like v9.0.0 (2022) and v3.x.x (2016-2018) are deprecated, as recent updates dropped legacy browser/Node.js support[1][4]. For new projects, v11.1.0 is recommended for security, cross-platform compatibility, and RFC9562 compliance[4][6].

Citations:


Incorrect Recommendation – uuid Version 11.1.0 is Stable

Based on the latest stable release information, uuid version 11.1.0 (released February 19, 2025) is fully stable, featuring improved TypeScript integration and modern crypto API support. The suggestion to downgrade to version 9.0.0 is not needed for production use.

  • Action Required: Keep the dependency as "uuid": "^11.1.0" in package.json.

Likely an incorrect or invalid review comment.


46-46:

❌ Incorrect review comment

Very recent ESLint version

ESLint 9.8.0 is a very recent version. Consider using a more established version to avoid potential compatibility issues with plugins and configurations.

-    "eslint": "^9.8.0",
+    "eslint": "^8.57.0",

🌐 Web query:

Is ESLint 9.x stable or still in beta/preview?

💡 Result:

ESLint 9.x is stable. The official v9.0.0 release was published on April 5, 2024[1][2], with subsequent updates including v9.9.0 (August 2024)[13], v9.14.0 (November 2024)[7], and v9.23.0 (latest in search results)[3]. Key indicators of stability include:

  • Official release documentation: A detailed migration guide exists for transitioning from ESLint 8.x to 9.x[2]
  • Production adoption: Community tutorials demonstrate real-world configurations for Vue3/TypeScript[4] and Next.js[8]
  • Extended feature development: Later 9.x versions added features like:
    • TypeScript configuration support (experimental in v9.9.0)[13]
    • Regular Expression Modifiers syntax support[7]
    • Performance improvements in config lookup[7]

While some frameworks like Next.js initially had compatibility issues[8], these were resolved through configuration overrides rather than ESLint instability. The ESLint team maintains active development with 15+ minor/patch releases in the 9.x series as of March 2025, following semantic versioning practices[6].

Citations:


No downgrade necessary; ESLint 9.x is fully stable.

After verifying the latest status of ESLint 9.x, the evidence confirms that ESLint 9 has been officially released—with multiple stable patch releases—and is backed by comprehensive migration guides and community adoption. Consequently, the suggestion to revert to ESLint 8.57.0 based on concerns about instability is no longer applicable.

Please continue using ESLint 9.8.0 as configured.

Likely an incorrect or invalid review comment.


14-14:

❓ Verification inconclusive

Verify Prisma 6 compatibility

Prisma 6 is very recent. Make sure your application is compatible with this version and that there are no breaking changes affecting your database schema.

Also applies to: 22-22


🌐 Web query:

Is Prisma 6 stable? Are there any breaking changes from Prisma 5?

💡 Result:

Prisma ORM 6 is stable, with multiple releases including 6.5.0 now available for production use[1][7]. Key upgrades include improved query performance, trace visibility, and deterministic schema generation. However, upgrading from Prisma 5 introduces several breaking changes:

Breaking Changes in Prisma 6

Node.js/TypeScript Requirements

  • Minimum Node.js versions:
    • 18.18.0 (Node 18)
    • 20.9.0 (Node 20)
    • 22.11.0 (Node 22)
  • Minimum TypeScript version 5.1.0
  • Node.js 16/17/19/21 no longer supported[2][5][9]

PostgreSQL Schema Changes

  • Implicit many-to-many relations now use PRIMARY KEY(A,B) instead of UNIQUE INDEX
  • Requires dedicated migration after upgrade[2][5]

API Changes

  • Replaced Buffer with Uint8Array for Bytes fields[5]
  • Removed NotFoundError in favor of PrismaClientKnownRequestError (code P2025)[5]
  • New reserved keywords (async, await, using) can't be model names[5]

Dependency Changes

  • Tracing now requires explicit @opentelemetry/api installation[1]
  • JSON protocol changes affecting error handling and response serialization[1][7]

Stability Notes

  • Tracing features have graduated from preview to stable status[1]
  • Schema generation is now deterministic, preventing unintended reordering[1]
  • Performance improvements include NOT EXISTS optimization for PostgreSQL relations[1][9]

To upgrade:

npm install @prisma/client@6 prisma@6 --save-exact
npx prisma migrate dev --name upgrade-to-v6

Always check the upgrade guide for specific migration steps[2][5][9]. While Prisma 6 is production-ready, some edge cases like VS Code IntelliSense performance regressions have been reported in specific versions (6.2.1/6.3.0)[13].

Citations:


Action: Verify Prisma 6 Compatibility

Prisma 6 (e.g., 6.5.0) is stable and production-ready. However, upgrading from Prisma 5 introduces several breaking changes. Please double-check the following:

  • Environment Requirements:

    • Ensure you’re running a supported Node.js version (minimum 18.18.0 for Node 18, 20.9.0 for Node 20, or 22.11.0 for Node 22) and TypeScript version (≥5.1.0).
  • Database Schema Changes:

    • Verify that any PostgreSQL schema changes (like the new handling of many-to-many relations with PRIMARY KEY(A,B)) are correctly managed with dedicated migrations.
  • API & Dependency Adjustments:

    • Update your code to accommodate API changes (e.g., handling Bytes fields with Uint8Array, recognizing that NotFoundError is replaced by PrismaClientKnownRequestError with code P2025).
    • If you use tracing, ensure dependencies like @opentelemetry/api are explicitly installed.

This verification applies to the dependency declarations at both line 14 and line 22 of your package.json.

Comment on lines +1 to +4
export class BaseUser {
email: string;
password: string;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for critical user properties.

The BaseUser class contains sensitive user data fields but lacks validation to ensure data integrity. Consider adding validation decorators or methods to ensure the email is a valid format and the password meets security requirements.

+import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';

 export class BaseUser {
+  @IsEmail()
+  @IsNotEmpty()
   email: string;
+  
+  @IsNotEmpty()
+  @MinLength(8)
   password: string;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export class BaseUser {
email: string;
password: string;
}
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
export class BaseUser {
@IsEmail()
@IsNotEmpty()
email: string;
@IsNotEmpty()
@MinLength(8)
password: string;
}

Comment on lines +2 to +3
email: string;
password: string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance security by not exposing password directly in model.

Storing the password as a plain string property is a security concern. Consider adding methods to handle password validation without direct property access, or at minimum documenting that this is a pre-hashed password.

Add documentation to clarify password handling or refactor to a more secure approach:

 export class BaseUser {
   email: string;
-  password: string;
+  // This should contain the hashed password only, never raw password
+  password: string;
+
+  // Method to validate password without exposing the hash
+  validatePassword(plainPassword: string): Promise<boolean> {
+    // Implementation would use a secure comparison method
+    // This would typically be moved to a service
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
email: string;
password: string;
export class BaseUser {
email: string;
// This should contain the hashed password only, never raw password
password: string;
// Method to validate password without exposing the hash
validatePassword(plainPassword: string): Promise<boolean> {
// Implementation would use a secure comparison method
// This would typically be moved to a service
}
}

Comment on lines +8 to +13
const prismaClientProvider = {
provide: PrismaClient,
useFactory: () => {
return new PrismaClient();
},
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance PrismaClient initialization with error handling and configuration.

The current PrismaClient initialization lacks error handling and environment-specific configuration. This could lead to difficult-to-debug connection issues in production.

 const prismaClientProvider = {
   provide: PrismaClient,
   useFactory: () => {
-    return new PrismaClient();
+    try {
+      const prisma = new PrismaClient({
+        log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
+      });
+      // Optional: Add connection validation
+      // await prisma.$connect();
+      return prisma;
+    } catch (error) {
+      console.error('Failed to initialize Prisma client:', error);
+      throw error;
+    }
   },
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prismaClientProvider = {
provide: PrismaClient,
useFactory: () => {
return new PrismaClient();
},
};
const prismaClientProvider = {
provide: PrismaClient,
useFactory: () => {
try {
const prisma = new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
// Optional: Add connection validation
// await prisma.$connect();
return prisma;
} catch (error) {
console.error('Failed to initialize Prisma client:', error);
throw error;
}
},
};

Comment on lines +4 to +10
export class GetUserUseCase {
constructor(private readonly authService: AuthService) {}

public async execute(user: LoginUser): Promise<AuthResponse> {
return await this.authService.login(user.email, user.password);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and input validation to the execute method.

The execute method lacks error handling and input validation. The method should validate inputs before passing them to downstream services and handle common errors specifically.

 export class GetUserUseCase {
   constructor(private readonly authService: AuthService) {}

   public async execute(user: LoginUser): Promise<AuthResponse> {
+    if (!user || !user.email || !user.password) {
+      throw new Error('Invalid user credentials provided');
+    }
+
+    try {
       return await this.authService.login(user.email, user.password);
+    } catch (error) {
+      // Consider more specific error handling based on error type
+      if (error.message === 'Invalid credentials') {
+        // Re-throw specific authentication errors
+        throw error;
+      }
+      // Handle unexpected errors
+      throw new Error(`Error authenticating user: ${error.message}`);
+    }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export class GetUserUseCase {
constructor(private readonly authService: AuthService) {}
public async execute(user: LoginUser): Promise<AuthResponse> {
return await this.authService.login(user.email, user.password);
}
}
export class GetUserUseCase {
constructor(private readonly authService: AuthService) {}
public async execute(user: LoginUser): Promise<AuthResponse> {
if (!user || !user.email || !user.password) {
throw new Error('Invalid user credentials provided');
}
try {
return await this.authService.login(user.email, user.password);
} catch (error) {
// Consider more specific error handling based on error type
if (error.message === 'Invalid credentials') {
// Re-throw specific authentication errors
throw error;
}
// Handle unexpected errors
throw new Error(`Error authenticating user: ${error.message}`);
}
}
}

Comment on lines +24 to +40
public async findByEmail(email: string): Promise<User | null> {
const user = await this.prisma.user.findUnique({
where: {
email,
},
});

if (!user) {
return null;
}

return {
id: user.id,
email: user.email,
password: user.password,
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for database operations.

The findByEmail method lacks error handling for database operations. If the Prisma query fails, it will result in an unhandled promise rejection that could crash the application.

   public async findByEmail(email: string): Promise<User | null> {
+    try {
       const user = await this.prisma.user.findUnique({
         where: {
           email,
         },
       });
 
       if (!user) {
         return null;
       }
 
       return {
         id: user.id,
         email: user.email,
         password: user.password,
       };
+    } catch (error) {
+      // Log the error
+      console.error('Error finding user by email:', error);
+      return null;
+    }
   }

Comment on lines +42 to +46
const createUserDto = {
email: 'test@test.com',
password: 'password123',
name: 'Test User',
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

DTO field mismatch with CreateUserDto implementation.

The name field is included in the test DTO but is not defined in the actual CreateUserDto class (which only has email and password as shown in the relevant code snippets). This might cause runtime errors.

 const createUserDto = {
   email: 'test@test.com',
   password: 'password123',
-  name: 'Test User',
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createUserDto = {
email: 'test@test.com',
password: 'password123',
name: 'Test User',
};
const createUserDto = {
email: 'test@test.com',
password: 'password123',
};

Comment on lines +5 to +6
abstract login(email: string, password: string): Promise<AuthResponse>;
abstract register(email: string, password: string): Promise<AuthResponse>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add support for additional user data in register method.

The register method only accepts email and password, but the controller tests show a name field is also being passed in the DTO. Consider expanding the method signature to include additional user data.

-  abstract register(email: string, password: string): Promise<AuthResponse>;
+  abstract register(email: string, password: string, userData?: Record<string, any>): Promise<AuthResponse>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
abstract login(email: string, password: string): Promise<AuthResponse>;
abstract register(email: string, password: string): Promise<AuthResponse>;
abstract login(email: string, password: string): Promise<AuthResponse>;
abstract register(email: string, password: string, userData?: Record<string, any>): Promise<AuthResponse>;

Comment on lines +1 to +65
import { cryptomktMock } from '@monorepo/core-domain';
import { cryptomktServiceMock, quotaServiceMock } from '@monorepo/core-domain-services';
import { CreateQuotaUseCase } from './create-quota';

describe('CreateQuotaUseCase', () => {
let useCase: CreateQuotaUseCase;

beforeEach(() => {
jest.clearAllMocks();
useCase = new CreateQuotaUseCase(cryptomktServiceMock, quotaServiceMock);
});

it('should create a quota successfully', async () => {
const input = {
from: 'BTC',
to: 'CLP',
amount: 1,
};

jest.spyOn(cryptomktServiceMock, 'get').mockResolvedValue(cryptomktMock);
jest.spyOn(quotaServiceMock, 'create').mockResolvedValue();

const result = await useCase.execute(input);

expect(result).toMatchObject({
from: input.from,
to: input.to,
amount: input.amount,
rate: parseFloat(cryptomktMock.price),
convertedAmount: input.amount * parseFloat(cryptomktMock.price),
});
expect(result.id).toBeDefined();
expect(result.timestamp).toBeDefined();
expect(result.expiresAt).toBeDefined();
expect(cryptomktServiceMock.get).toHaveBeenCalledWith('BTC', 'CLP');
expect(quotaServiceMock.create).toHaveBeenCalled();
});

it('should throw error when missing from field', async () => {
const input = {
to: 'CLP',
amount: 1,
};

await expect(useCase.execute(input)).rejects.toThrow('Both "from" and "to" fields are required.');
});

it('should throw error when missing to field', async () => {
const input = {
from: 'BTC',
amount: 1,
};

await expect(useCase.execute(input)).rejects.toThrow('Both "from" and "to" fields are required.');
});

it('should throw error when missing amount field', async () => {
const input = {
from: 'BTC',
to: 'CLP',
};

await expect(useCase.execute(input)).rejects.toThrow('"amount" field is required.');
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test for service error handling.

The test suite doesn't cover the scenario where the service calls might fail. Consider adding a test that verifies errors from the services are properly propagated.

it('should propagate errors from the cryptomkt service', async () => {
  const input = {
    from: 'BTC',
    to: 'CLP',
    amount: 1,
  };

  const expectedError = new Error('Service unavailable');
  jest.spyOn(cryptomktServiceMock, 'get').mockRejectedValue(expectedError);

  await expect(useCase.execute(input)).rejects.toThrow(expectedError);
});

it('should propagate errors from the quota service', async () => {
  const input = {
    from: 'BTC',
    to: 'CLP',
    amount: 1,
  };

  jest.spyOn(cryptomktServiceMock, 'get').mockResolvedValue(cryptomktMock);
  const expectedError = new Error('Database error');
  jest.spyOn(quotaServiceMock, 'create').mockRejectedValue(expectedError);

  await expect(useCase.execute(input)).rejects.toThrow(expectedError);
});

Comment on lines +27 to +31
model User {
id String @id @default(uuid())
email String @unique
password String
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance the User model with additional fields and comments.

The User model is missing important fields for tracking user activities and lacks comments about password storage. Consider adding:

  1. Audit fields (createdAt, updatedAt)
  2. A comment indicating that password should be stored as a hash, not plaintext
  3. Additional fields like lastLogin if needed by the application
model User {
  id       String @id @default(uuid())
  email    String @unique
-  password String
+  password String  // Stored as a hash, not plaintext
+  createdAt DateTime @default(now())
+  updatedAt DateTime @updatedAt
+  lastLogin DateTime?
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
model User {
id String @id @default(uuid())
email String @unique
password String
}
model User {
id String @id @default(uuid())
email String @unique
password String // Stored as a hash, not plaintext
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastLogin DateTime?
}

Comment on lines +8 to +16
public async execute(user: CreateUser): Promise<AuthResponse> {
const existingUser = await this.authService.get(user.email);

if (existingUser) {
throw new ConflictException('User already exists');
}

return await this.authService.register(user.email, user.password);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for email and password.

The execute method doesn't validate the input data. Consider adding validation to ensure the email is in a valid format and the password meets security requirements before attempting to create the user.

public async execute(user: CreateUser): Promise<AuthResponse> {
+  // Validate email format
+  if (!user.email || !this.isValidEmail(user.email)) {
+    throw new Error('Invalid email format');
+  }
+
+  // Validate password requirements
+  if (!user.password || !this.isStrongPassword(user.password)) {
+    throw new Error('Password does not meet security requirements');
+  }
+
  const existingUser = await this.authService.get(user.email);

  if (existingUser) {
    throw new ConflictException('User already exists');
  }

  return await this.authService.register(user.email, user.password);
}
+
+private isValidEmail(email: string): boolean {
+  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+  return emailRegex.test(email);
+}
+
+private isStrongPassword(password: string): boolean {
+  // Require minimum length and complexity
+  return password.length >= 8 && 
+         /[A-Z]/.test(password) && 
+         /[a-z]/.test(password) && 
+         /[0-9]/.test(password);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public async execute(user: CreateUser): Promise<AuthResponse> {
const existingUser = await this.authService.get(user.email);
if (existingUser) {
throw new ConflictException('User already exists');
}
return await this.authService.register(user.email, user.password);
}
public async execute(user: CreateUser): Promise<AuthResponse> {
// Validate email format
if (!user.email || !this.isValidEmail(user.email)) {
throw new Error('Invalid email format');
}
// Validate password requirements
if (!user.password || !this.isStrongPassword(user.password)) {
throw new Error('Password does not meet security requirements');
}
const existingUser = await this.authService.get(user.email);
if (existingUser) {
throw new ConflictException('User already exists');
}
return await this.authService.register(user.email, user.password);
}
private isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
private isStrongPassword(password: string): boolean {
// Require minimum length and complexity
return password.length >= 8 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/[0-9]/.test(password);
}

@RodrigooDS RodrigooDS closed this Mar 30, 2025
@RodrigooDS RodrigooDS deleted the feature/monorepo branch March 30, 2025 23:46
@RodrigooDS RodrigooDS restored the feature/monorepo branch March 30, 2025 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant